Best way to loop through array object - javascript

leagueInfo = {"data":[{"tier":"Gold"},{"tier":"Bronze"}]}
So far I have been doing 2 for loops like this:
for (const key of Object.keys(leagueInfo)) {
console.log('5on5 ranked', leagueInfo[key]);
// Array (2) is output
for (const values of leagueInfo[key]) {
console.log('5on5 ranked', values.tier );
// Output is :
// Gold
// Bronze
}
}
Do I really need 2 loops or is there a shorter way of doing this?

leagueInfo.data.forEach(item => console.log(item.tier));

There are several ways.
You could use methods from the lodash or underscore libraries, that are replicas of how the .foreach or for loops work.
If the data that you have is always the same and similar to the one posted you can do the following to iterate through the data items that you have in the array. Keep in mind that the first iteration you are doing is useless, since you could access the property directly.
var leagueInfo = {"data":[{"tier":"Gold"},{"tier":"Bronze"}]}
leagueInfo.data.forEach((item) => {
console.log(item);
console.log(item.tier);
})

There is dozens of ways to iterate objects or arrays. And usually with functions specifically adapted for certain goals in mind. if you want to only console.log iteration result you can use .map()
var leagueInfo = {"data":[{"tier":"Gold"},{"tier":"Bronze"}]};
Object.values(leagueInfo).map(function(dataArray) {
console.log('5on5 ranked', dataArray);
dataArray.map(function(values) {
console.log('5on5 ranked', values.tier );
})
})
And here's a link to W3Schools where you can find all possible actions with arrays.
https://www.w3schools.com/jsref/jsref_obj_array.asp

Related

Prevent pushing to array if duplicate values are present

I'm mapping an array and based on data i'm pushing Option elements into an array as follows
let make_children: any | null | undefined = [];
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { make: {} | null | undefined; }, key: any) => {
make_children.push(
<Option key={key}>{item.make}</Option>
);
});
Following data array has several objects and these objects have an attribute called model.
buyerActivityResult.simulcastMyAccount.data
I want to prevent pusing Options to my array if the attribute model has duplicate data. It only has to push once for all similar model values.
How can i do it?
I tried something like this
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { model: {} | null | undefined; }, key: any) => {
model_children.indexOf(item.model) === -1 && model_children.push(
<Option key={key}>{item.model}</Option>
);
});
But still duplicate values are being pushed into my array.
Its difficult to tell what you are trying to achieve but it looks like a map may not be the right tool for the job.
A map returns the same sized length array as that of the original array that you are calling map on.
If my assumptions are correct, your buyerActivityResult.simulcastMyAccount.data array has duplicate values, and you want to remove these duplicates based on the model property? One way to achieve this would be to use the lodash library for this, using the uniq function:
const uniqueResults = _.uniq(buyerActivityResult.simulcastMyAccount.data, (item) => item.model);
The Array.prototype.map() method is supposed to be used for manipulating the data contained into the array performing the operation. To manipulate data from other variables I recommend to use a for-loop block.
If item.model is an object, the function Array.prototype.indexOf() always returns -1 because it compares the memory address of the objects and does not do a deep comparison of all properties values.
The usual solution to remove duplicate data from an array is converting the Array into a Set then back to an Array. Unfortunately, this works only on primary type values (string, number, boolean, etc...) and not on objects.
Starting here, I will review your source code and do some changes and explain why I would apply those changes. First of all, assuming the make_children array does not receive new attribution later in your code, I would turn it into a constant. Because of the initialization, I think the declaration is overtyped.
const make_children: any[] = [];
Then I think you try to do too much things at the same time. It makes reading of the source code difficult for your colleagues, for you too (maybe not today but what about in few weeks...) and it make testing, debugging and improvements nearly impossible. Let's break it down in at least 2 steps. First one is transforming the data. For example remove duplicate. And the second one create the Option element base on the result of the previous operation.
const data: { make: any }[] = buyerActivityResult?.simulcastMyAccount?.data || [];
let options = data.map((item) => !!item.model); // removing items without model.
// Here the hard part, removing duplicates.
// - if the models inside your items have a property with unique value (like an ID) you can implement a function to do so yourself. Take a look at: https://stackoverflow.com/questions/2218999/remove-duplicates-from-an-array-of-objects-in-javascript
// - or you can use Lodash library like suggested Rezaa91 in its answer
options = _.uniq(data, (item) => item.model);
Now you only have to create the Option elements.
for (var i = 0; i < options.length; i++) {
model_children.push(<Option key={i}>{options[i].model}</Option>);
}
// OR using the Array.prototype.map method (in this case, do not declare `model_children` at the beginning)
const model_children:[] = options.map((opt:any, i:number) => <Option key={i}>{opt.model}</Option>);
Despite the lack of context of the execution of the code you provided I hope my answer will help you to find a solution and encourage you to write clearer source code (for the sake of your colleagues and your future self).
PS: I do not know anything about ReactJs. forgive me my syntax mistakes.

Iterating through Objects of Objects Javascript?

I have this object structure that I want to iterate through to make a node of all the "properties." So I want to make nodes for the objects 1,2,5, and 8 but not for the arrays. I have this piece of code and was wondering why Object.keys() for each of response's properties is [0] instead of [2,5] or [9,12]?
const response = {
'1':{
'2':['3','4'],
'5':['6','7']
},
'8':{
'9':['10','11'],
'12':['13','14']
},
};
for(const property in response){
if(!response.hasOwnProperty(property)) continue;
console.log(property) // prints 1 and 8
g.addNode(property);
console.log(Object.keys(property)) // [0] instead of [2,5] or [9,12]
for(const prop in property){
if(!property.hasOwnProperty(prop) || prop === 0) continue;
console.log(prop)
g.addEdge(prop,property, {directed:true})
}
}
EDIT: this loop works :)
for(const property in resp){
g.addNode(property);
for(const prop in resp[property]){
g.addEdge(property,prop, {directed:true})
}
}
I've run into this exact issue with dictionaries, and it's good you're running into this issue relatively quickly because it can get pretty hard to debug once you get further. Basically, you're iterating over a dictionary with a for..in loop, which doesn't exactly do what you want it to. Basically, each item you iterate over (property in this case) isn't the value of the dictionary ({'2':['3','4'], '5':['6','7']}), it's a full dictionary item that includes both key and value. This is practically useless to you, so it's not particularly helpful. There are a couple things you can do, one of which still uses a forEach, except somewhat differently:
Object.keys(response).forEach(function(key) {
// do your actions here
// key is your dictionary key (1, 8)
// response[key] will have your value ({'2':['3','4'], '5':['6','7']})
});
This should work, and you can put the rest of your code to use this.

Nested Object.keys() are printing properties multiple times instead of only once

I have two objects that I need to loop through so I can use their properties later on. However if I print the variables each of them is printed twice. I understand that because I have the Object.keys() inside other Object.keys(). Is there any way to loop through these two objects and only get each variable one time?
My code:
Object.keys(newData.name).map(async key => {
Object.keys(newData._temp.images).map(async keyImage => {
console.log(newData.name[key].name,'printed 2x instead of once');
console.log(newData._temp.images[keyImage].rawFile.preview, 'printed 2x instead of once');
});
});
Thank you in advance.
your logic here of nesting the loops is wrong.
these 2 object does not seem to be connected to one another, meaning you do not need the data from the first loop in order to perform the other loops. just split it into 2 seperate loops, would save you both time and repititions:
let nameKeys = Object.keys(newData.name).map(key => newData.name[key].name);
let imagesKeys = Object.keys(newData._temp.images).map(keyImage =>
newData._temp.images[keyImage].rawFile.preview);
now you can access nameKeys and imageKeys whenever you want, and they will contain the values you previously logged. My naming might be a bit off tho, feel free to change that :D
Also, as others mentioned- no need for the async keyword... you do not perform any async operation inside (yet, at least. if thats what you're planning then go ahead and keep it).
These iterators do not need to be nested. The second iterator is not looping through an item of the first iterator.
Object.keys(newData.name).forEach(key => {
console.log(newData.name[key].name);
});
Object.keys(newData._temp.images).forEach(keyImage => {
console.log(keyImage[keyImage].rawFile.preview);
});
If you are only iterested in outputting data, then .map() is not the right function to use because this is used when you care about the return value. Use .forEach() if you just want to loop through things.
Also, the async keyword is not needed here.. unless you plan to do some async/await stuff in the loops later!
You could iterate over the indices once and then access the values in both arrays:
const names = Object.keys(newData.name);
const images = Object.keys(newData._temp.images);
for(let i = 0; i < Math.min(names.length, images.length); i++) {
const name = names[i];
const image = images[i];
//...
}

How to use underscore .groupBy function to group an array of json objects based on multiple properties?

I have an array of json objects who results I want to groupBy() based on multiple properties i.e.,
I have:
[
{
prop1:val1,
prop2:val2,
prop3:val3,
prop4:val4
}
]
Now if I just wanted to group by prop1 I guess I could have done :
_.groupBy(givenArray, 'prop1');
Now what should I do if I have to group by prop1,prop2 and prop3, i.e., (prop1 && prop2 && prop3)
Please guide.
You can put that target values to an array, and then form them to string, or just transform them to string form and combine:
_.groupBy(givenArray, function(item) {
var keys = _.pick(item, 'prop1', 'prop2', 'prop3');
// If all are string or number,
// return keys.join('_#%#_'); // this prevent ['ab', 'a'] generate same key to ['a', 'ba'];
return JSON.stringify(keys);
});
JSON.stringify maybe one of the many ways to create a combined key, I'm not sure what your vals is (string, number or else), so I'll just use stringify here.
It really depends on what you want your final structure to be.
If you don't mind a non-flat object structure, you can do nested _.groupBy calls:
var result = _(givenArray) //begin chain
.groupBy('a')
.mapValues(function(groupedByA) {
return _(groupedByA) //begin chain
.groupBy('b')
.mapValues(function (groupedByAAndB) {
return _.groupBy(groupedByAAndB, 'c');
})
.value(); //end chain
})
.value(); //end chain
//Then you can do things like:
result[5][4][3]; //All items where a=5, b=4, and c=3.
Downside here is that there's an extra level of nesting for each property you group by, and result[5][4] will blow up if there aren't any results where a=5 for example. (Though you could use a library like koalaesce for that)
Alternatively, if you want a flat object structure, but don't mind it being a bit ungainly to access the items, then:
var result = _.groupBy(givenArray, function (item) {
return JSON.stringify(_.pick(item, 'a','b','c'));
});
//Much simpler above, but accessed like:
result[JSON.stringify({a: 5, b:4, c:3})]
Much simpler and scales better to grouping by more things... but awkward to work with, since the keys are full JSON strings; but then you also don't have the null issue that the first option has.
You can also, just use _.values or some equivalent to turn the flat object structure into a single array of arrays. Then there's obviously no "random" access to get all items with a given value of a,b, and c, but you can always loop over it.

Most efficient way to filter through Javascript array of objects without standard keys

Let's say I have this (admittedly bad) structure:
var items = [
{SomeString: [value1,value2...]},
{AnotherString: [valueX,valueY...]},
{YetAnotherString: [valueA,valueB...]}
];
I want to obtain the array assigned to "AnotherString".
One way (via jQuery) I am using:
$.each(items,function(x,item){
$.each(item,function(key,values){
if(key=="AnotherString"){
//...do something
}
}
});
Obviously this is terribly inefficient, as it loops through needless items.
One way would be to refactor it in a "while(!found)" loop.
My question: what's the most efficient way to filter through such an array?
Edit: forgot to clarify that I do not know in advance the key strings. This is used in an external loop where the key strings I am looking for are passed down.
Sometimes, stripping away jQuery and using Vanilla JS is the route to the most efficient solution.
items.some(function(item) {
if( item.hasOwnProperty("AnotherString")) {
// do something with item["AnotherString"]
return true;
}
return false;
});
Documentation on the some function - Polyfill provided
If you know that this structure is bad, why do you stick to it? Just use a normal object:
var obj = {
SomeString: [value1,value2...],
AnotherString: [valueX,valueY...],
YetAnotherString: [valueA,valueB...]
};
If this is not possible for some reason, convert it dynamically:
obj = {};
items.forEach(function(item) {
var key = Object.keys(item)[0];
obj[key] = item[key];
});
This returns an object like the above. Once you have this, just do obj[someKey] when needed. No loops.
(This assumes, of course, that all keys are different).

Categories

Resources