name an object in Array - javascript

I'm doing multiple fetchs with Promise.all. So I receive data like this :
[
0: {
...
},
1: {
...
}
]
But I would like to name my Objects. So I can do data.myObject istead of data[0].
I would like the index to be a string that I chose.
For example, i'd like to get :
[
"home": {
...
},
"product": {
...
}
]
Is is possible ? Thanks

No, this is not possible. Promise.all works on iterables (like arrays), not on objects. Name your values after the Promise.all call:
const data = await Promise.all([…, …]);
const home = data[0], product = data[1];
becomes with destructuring
const [home, product] = await Promise.all([…, …]);

Promise.all works with an array. Arrays deal in numerically indexed (ordered) values.
You'd need to have a name stored somewhere for each promise, and then generate the object from them once the promises had resolved.
e.g.
const names = ['home', 'product'];
const promises = [fetchHome(), fetchProduct()];
const results = await Promise.all(promises);
const resultsByName = names.reduce((prev, curr, index) => {
return {...prev, [curr]: results[index]};
}, {});
You could use a similar approach without the second array if the name was available in the resolved values of fetchHome() and fetchProduct().

You can destructure the array of Promise results in the Promise.all.
const [home, product, toolbar, nav] = await Promise.all([
getHome(), getProduct(), getToolBar(), getNav()
]);
Since the results are an array like anything else you can destructure arrays and even use the ...rest syntax:
const [home, product, toolbar, nav, ...otherPromises] = await Promise.all([
getHome(), getProduct(), getToolBar(), getNav(), getOtherThing1()
]);
// otherPromises will be an array that you'll have to access
// with numeric keys as before:
// eg. otherPromises[0] might be the first non-named promise
// the result of getOtherThing1()

Seems like you want to convert Array to an object so you can get data by calling a key.
const arr = [ { id: "home", val: "val1" }, { id: "product", val: "val2" }];
const convert = arr.reduce((a,b) => (a[b.id] ??= b,a),{});
console.log(convert);
console.log(convert.home);

Related

Can destructuring an array be used to map properties of each elements?

Suppose there is an array like this:
const a = [ {p:1}, {p:2}, {p:3} ];
Is it possible to destructure this array in order to obtain p = [1, 2, 3] ?
Because this does not work :
const [ ...{ p } ] = a; // no error, same as const p = a.p;
// p = undefined;
Edit
In response to all the answers saying that I need to use Array.prototype.map, I am aware of this. I was simply wondering if there was a way to map during the destructuring process, and the answer is : no, I need to destructure the array itself, then use map as a separate step.
For example:
const data = {
id: 123,
name: 'John',
attributes: [{ id:300, label:'attrA' }, { id:301, label:'attrB' }]
};
function format(data) {
const { id, name, attributes } = data;
const attr = attributes.map(({ label }) => label);
return { id, name, attr };
}
console.log( format(data) };
// { id:123, name:'John', attr:['attrA', 'attrB'] }
I was simply wondering if there was a way, directly during destructuring, without using map (and, respectfully, without the bloated lodash library), to retrive all label properties into an array of strings.
Honestly I think that what you are looking for doesn't exist, normally you would map the array to create a new array using values from properties. In this specific case it would be like this
const p = a.map(element => element.p)
Of course, there are some packages that have many utilities to help, like Lodash's map function with the 'property' iteratee
you can destructure the first item like this :
const [{ p }] = a;
but for getting all values you need to use .map
and the simplest way might be this :
const val = a.map(({p}) => p)
Here's a generalized solution that groups all properties into arrays, letting you destructure any property:
const group = (array) => array.reduce((acc,obj) => {
for(let [key,val] of Object.entries(obj)){
acc[key] ||= [];
acc[key].push(val)
}
return acc
}, {})
const ar = [ {p:1}, {p:2}, {p:3} ];
const {p} = group(ar)
console.log(p)
const ar2 = [{a:2,b:1},{a:5,b:4}, {c:1}]
const {a,b,c} = group(ar2)
console.log(a,b,c)

How can we create OBJ property with the same name from array values?

Hello I'm trying to create an object that includes under the same property name a bunch of array values,
This what I'm trying
const quiz = [
{question: 'Who is the main character of DBZ',
options: ['Vegetta','Gohan','Goku']}
]
const newObj = {
options: []
}
quiz.forEach((item)=>{
item.options.forEach((item, index)=>{
newObj.options[`name${index}`] = item
})
})
expected value =
newObj = {
options: [{name: 'Vegetta'},{name:'Gohan'},{name:'Goku'}]
}
actual value received =
newObj = {
{ options: [ name0: 'Vegetta', name1: 'Gohan', name2: 'Goku' ] }}
Thanks in advance!
As you've noticed, newObj.options[`name${index}`] = item creates a new key on your options array, and sets that to item. You instead want to push an object of the form {name: item} into your array. There are a few ways you could go about this, one way is to use .push() like so:
quiz.forEach((item)=>{
item.options.forEach((item)=>{
newObj.options.push({name: item});
})
});
while not as common, you can also use set the current index of options, which is slightly different to the above example, as it will maintain the same index, which can be important if quiz is a sparse array that you want to keep the same indexing of on options:
quiz.forEach((item)=>{
item.options.forEach((item, index)=>{
newObj.options[index] = {name: item};
})
});
Example of the difference:
const arr = [1, 2,,,5]; // sparse array
const pushRes = [];
const indxRes = [];
arr.forEach(n => pushRes.push(n));
arr.forEach((n, i) => indxRes[i] = n);
console.log("Push result", pushRes);
console.log("Index result", indxRes);
For a different approach, you also have the option of using something like .flatMap() and .map() to create your options array, which you can use to create newObj:
const quiz = [
{question: 'Who is the main character of DBZ',
options: ['Vegetta','Gohan','Goku']}
];
const options = quiz.flatMap(({options}) => options.map(name => ({name})));
const newObj = {options};
console.log(newObj);

JS find array of String and Array Object without duplicates

I had a lists of duplicate array of String and array of object. I want to find the property into one particular object of array uniquely without duplication of array object.
If can done in lodash library, it would be awesome.
const arr1 = ['test#email', 'test2#email']
const arr2 = [{ id: 1, email: 'test#email' }]
Expected result
['test2#email']
this is what I done so far
By turning arr1 into object frist so I can compare with arr2
const emailLists = _.map(arr, function (item) {
console.log(item)
return { email: item }
})
then merge them to together and used uniq to remove duplicates
const merge = _.unionBy(array1, array2, 'email')
const result _.uniq(merge, 'email');
I think it still not a good process and not clean
You can do it without lodash
const arr1 = ['test#email', 'test2#email']
const arr2 = [{ id: 1, email: 'test#email' }]
const emailToDelete = arr2.map(a => a.email)
const result = arr1.filter(e => !emailToDelete.includes(e))
console.log(result)
You can use lodash chain to solve that:
const result = _([...arr1, ...arr2.map(i => i.email)]) // ['test#email', 'test2#email', 'test#email']
.countBy() // { test#email: 2, test2#email: 1 }
.pickBy(count => count === 1) // { test2#email: 1 }
.keys() // ['test2#email']
.value();
You can use filter on the first array and check that an email address matches with some of your objects in the second array. So even if it matches with multiple objects, the matching email will only be in the output once.
I want to find the property into one particular object of array uniquely without duplication of array object
So then your expected output is wrong, as you provide an email that is not found in the object array. I guess that was a typo in your question.
const arr1 = ['test#email', 'test2#email']
const arr2 = [{ id: 1, email: 'test#email' }]
const result = arr1.filter(search => arr2.some(({email}) => email == search));
console.log(result);

How can you convert a List<Pair<String,String>> to List<String> in javascript?

This is the Output: The data structure for the output is Map<String, List<Pair<String, String>>>
"testdata": [
{
"1.0": "True"
},
{
"1.1": "False"
}
]
Now I need to display this data on the UI as "testdata":["1.0","1.1","1.2"],
wherein here from the Pair, I want to fetch only the first elements from the Pair and put them in the structure Map<String, List<String>>
So how do I write that code in javascript in order to get that output?
this.previousData = versions.filter(v => v !== this.version).map(item => ({ text: item, value: item }))
How do I modify this code to get this output "testdata":["1.0","1.1","1.2"]?
You could try something like this:
// This assumes that there will be only one key in each array item, or the first key in each array item denotes the version
this.previousData =
versions
.filter(v => v !== this.version)
.map(item => Object.keys(item)[0])
// or as MikeT suggested, if you are only expecting something like this:
// [
// { "v1": "some value 1" },
// { "v2": "some value 2" },
// ... in general --> { "version": "some value" }
// ]
// you may try this as well
this.previousData =
versions
.filter(v => v !== this.version)
.map(item => Object.keys(item))
.flat()
your question isn't too clear about which element you are struggling with
so to get the data
async getData()
{
const resp = await fetch("<<your url from your java service>>" )
return await resp.json();
}
to format the data as you wish it to be formatted
formatData(json){
return json.testdata.map((i)=>Object.entries(i).map(([k,v])=>k)).flat()
}
and to display in vue
<template>testdata:{{testdata}}</template>
<script>
...
methods:{
async populatedata(){
const tmp = await getData()
this.testdata = formatData(tmp)
}
}
...
</script>
Object.entries will convert an object into a tuple array so {"1.0":"True","1.1":"False"} will become [["1.0","True"],["1.1":"False"]] which then lets you use tuple decomposition to get the keys
you could also use Object.keys() if you have no need for the values but that wasn't clear from the context so i gave you the more flexible option

How to approach multiple async calls on single array?

this is bit more theoretical question. I originally intented to call this question Is it possible to iterate over map twice, but just from the sound of it, it sounds like an anti-pattern. So I assume I'm just approaching this wrong.
Also note: Data here servers as an abstraction. I know that what I'm doing here with data provided here is unnecessary, but please don't get fixated too much on data-structure and what-not. It does not represent the real (more complex, which furthermore is provided by client and I can't alter) data I'm working with. Instead approach the problem as how to return structured async calls for each array item please! :-)
My problem boils down to this:
I have array of ids on which I need to execture two separate asynchronous calls
Both of these callls need to pass (and in all id instances)
So as an example, imagine I have these two data-sets:
const dataMessages = [
{ id: "a", value: "hello" },
{ id: "b", value: "world" }
];
const dataNames = [
{ id: "a", name: "Samuel" },
{ id: "b", name: "John" },
{ id: "c", name: "Gustav"},
];
And an API-call mock-up:
const fetchFor = async (collection: Object[], id: string): Promise<Object> => {
const user = collection.find(user => user.id === id);
if (!user) {
throw Error(`User ${id} not found`);
}
return user;
};
Now I need to call the fetchFor() function for both the data-sets, presumably inside the inside the Promise.all, given forEach is not asynchronous from a predetermined array of ids.
I was thinking something akin to maping a list of Promises for the Promise.all to execute. This works fine, when you only need to map a single api-call:
const run = async () => {
const result = await Promise.all(
['a', 'b'].map(id => fetchFor(dataMessages, id)
)
console.log(result) // [{id: 'a', value: 'hello'}, {id: 'b', value: 'world}]
}
However I somehow need to return both promises for the
fetchFor(dataMessages, id)
fetchFor(dataNames, id)
inside the Promise.all array of Promises.
I guess I could always simply do a flatMap of two maps for both instances of API calls, but that sounds kinda dumb, given
I'd be doing array.map on same array twice
My data structure would not be logically connected (two separate array items for the same user, which would not even by followed by eachother)
So ideally I'd like to return dat in form of
const result = await Promise.all([...])
console.log(result)
/* [
* {id: 'a', message: 'hello', name: 'Samuel'},
* {id: 'b', message: 'world', name: 'John'},
* ]
Or do I simply have to do flatmap of promises and then do data-merging to objects based on id identifier inside a separate handler on the resolved Promise.all?
I've provided a working example of the single-api-call mockup here, so you don't have to copy-paste.
What would be the correct / common way of approaching such an issue?
You could nest Promise.all calls:
const [messages, names] = await Promise.all([
Promise.all(
['a', 'b'].map(id => fetchFor(dataMessages, id)
),
Promise.all(
['a', 'b', 'c'].map(id => fetchFor(dataNames, id)
)
]);
If you're wanting to then merge the results after retrieved, it's just a matter of standard data manipulation.

Categories