How to loop through an array containing objects of objects - javascript

This is my array:
[{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
}
}]
I need to find the last element (his index) who has the skill good set to true. The only way I know to fix this is to use a for/if combination. Is there any other faster/optimal way to do it ?

Use filter:
const goodSkills = myArray.filter(x => x.skills.good)
Then get the last item:
goodSkills[goodSkills.length - 1]
Or if you only need the index, and we treat name as a unique key:
const lastGoodIndex = myArray.findIndex(x => x.name === goodSkills[goodSkills.length - 1].name)
You can then use lastGoodIndex for whatever nefarious purpose you have in mind.
Alternatively if name is not a unique key, I suggest just using forEach:
let lastGoodIndex;
myArray.forEach((x, i) => lastGoodIndex = x.skills.good ? i : lastGoodIndex);
console.log(lastGoodIndex);

The fastest way is to us a for loop:
var arr = [{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
},
}]
function findLastGoodIndex(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i].skills.good) {
return i;
}
}
}
console.log(findLastGoodIndex(arr));
Or if the list isn't that large you can combine reverse with findIndex:
arr.reverse().findIndex(x => x.skills.good));

You can do it in 3 rows:
var arr = [
{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
},
}
]
var lastIndex;
arr.forEach(function(item, i) {
if(item.skills.good) lastIndex = i;
})
console.log(lastIndex);

If you want the index of last element that has good = true your best option is to use for/if (perhaps going backwards, i.e. i-- if you can guess where can you expect the good item?).
filter will find the item(s) too, and it also has index property, but generally it's slower than for/if.
forEach will also generally be slower than for/if.
edit:styling.

Since you need to search the last value, you should search in descending order. This would prevent any unnecessary iterations.
var data = [{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
}
}
];
var i = data.length;
var result = null;
while (i--) {
if (data[i].skills.good) {
result = i;
break;
}
}
console.log(result);

Related

Javascript doesn't sort list items properly

I have - array of objects - list items, I sort these items by fieldName. Normally it seems it works fine, but on some items it behaves strange and doesn't sort items properly.
Here is the code that I am making sorting:
elements.slice(0).sort((a, b) => {
if (a[fieldName] === '' || a[fieldName] == null) return 1;
if (b[fieldName] === '' || b[fieldName] == null) return -1;
return (
itemSort
? a[fieldName]?.toLowerCase() < b[fieldName]?.toLowerCase()
: a[fieldName]?.toLowerCase() > b[fieldName]?.toLowerCase()
)
? 1
: -1;
})
itemSort is a boolean and I decide to make A-Z or Z-A sorting.
Here is a picture from strange behaviour, I only see the wrong sorting on these items.
Here is an example of elements
[
{
icon: "IssueTracking"
id: "62a0868c2b2b180061ab05d8"
name: "[DEMO ASLC] All Issues"
type: "sheet"
updatedAt: "2022-12-05T15:17:23.072Z"
url: "/admin/documents/edit/62a0868c2b2b180061ab05d8"
},
{
icon: "..."
id: "..."
name: "..."
type: "..."
updatedAt: "..."
url: "..."
},
...
]
.sort() method modifies array itself, so you need to copy the array into a new array if you would like to keep your original array order in place.
const elementArray = [
{ name: "abc" },
{ name: "abb" },
{ name: "cc" },
{ name: "1bb" },
{ name: "4bc" },
{ name: "abb4" },
{ name: "" },
];
const sortItems = (elements, asc = true) => {
const sortedArray = [...elements];
sortedArray.sort((a, b) => {
let sortResult = a.name?.toLowerCase() > b.name?.toLowerCase() ? 1 : -1;
return asc ? sortResult : sortResult * -1
});
return sortedArray;
};
console.log(`descending: ${JSON.stringify(sortItems(elementArray, false))}`);
console.log(`ascending: ${JSON.stringify(sortItems(elementArray))}`);
One of the best way to do this is to use the Lodash sortBy method.
You can install this library with npm or yarn and simply do _.sortBy(elements, 'fieldName')

How to remove a nested array object in JS?

I am trying to remove the first object from a nested array but somehow I am not able to delete the first object here is my code can you please help?
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
},
];
var ind = arr.findIndex(function (element) {
return element.demo?.id === "NOT";
});
if (ind !== -1) {
arr.splice(ind, 1);
}
console.log('this is new', arr);
If you have any better solution then feel free to drop will appreciate your help.
You are accessing the wrong array dimension. Check each subarray in the array:
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
},
];
for (const item of arr) {
const index = item.demo.findIndex(subitem => subitem.id === "NOT");
if (index >= 0) item.demo.splice(index, 1)
}
console.log('this is new', arr);
just add the below snippet:
const newArr = arr.map(data => {
return { demo : data?.demo.filter(d => d.id != "NOT") }
})
console.log(newArr)
Explanation :
Here, I'm looping through each main array, entering into objects, and then filtering only those with id other than "NOT".
Comment if you stuck with anything in the above code.
As per my understanding after looking in your code, You want to filter out the object which contains id as NOT. If Yes, You can simply achieve that by using Array.filter() method.
Live Demo :
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
}];
const res = arr.map(({ demo }) => demo.filter(({ id }) => id !== 'NOT'));
console.log(res);

Create unique values from duplicates in Javascript array of objects

I have an array of duplicated objects in Javascript. I want to create an array of unique objects by adding the index of occurrence of the individual value.
This is my initial data:
const array= [
{name:"A"},
{name:"A"},
{name:"A"},
{name:"B"},
{name:"B"},
{name:"C"},
{name:"C"},
];
This is expected end result:
const array= [
{name:"A-0"},
{name:"A-1"},
{name:"A-2"},
{name:"B-0"},
{name:"B-1"},
{name:"C-0"},
{name:"C-1"},
];
I feel like this should be fairly simple, but got stuck on it for a while. Can you please advise how I'd go about this? Also if possible, I need it efficient as the array can hold up to 1000 items.
EDIT: This is my solution, but I don't feel like it's very efficient.
const array = [
{ name: "A" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
const sortedArray = _.sortBy(array, 'name');
let previousItem = {
name: '',
counter: 0
};
const indexedArray = sortedArray.map((item) => {
if (item.name === previousItem.name) {
previousItem.counter += 1;
const name = `${item.name}-${previousItem.counter}`;
return { name };
} else {
previousItem = { name: item.name, counter: 0};
return item;
}
});
Currently you are sorting it first then looping over it, which may be not the most efficient solution.
I would suggest you to map over it with a helping object.
const a = [{name:"A"},{name:"A"},{name:"A"},{name:"B"},{name:"B"},{name:"C"},{name:"C"},], o = {};
const r = a.map(({ name }) => {
typeof o[name] === 'number' ? o[name]++ : o[name] = 0;
return { name: `${name}-${o[name]}` };
});
console.log(r);
Keep a counter, and if the current name changes, reset the counter.
This version mutates the objects. Not sure if you want a copy or not. You could potentially sort the array by object name first to ensure they are in order (if that's not already an existing precondition.)
const array = [
{ name: "A" },
{ name: "A" },
{ name: "A" },
{ name: "B" },
{ name: "B" },
{ name: "C" },
{ name: "C" },
];
let name, index;
for (let i in array) {
index = array[i].name == name ? index + 1 : 0;
name = array[i].name;
array[i].name += `-${index}`;
}
console.log(array);
Another way, if you don't want to sort, and don't want to mutate any objects, is to use a map and keep track of the current index for each object.
const array = [
// NOTE: I put the items in mixed up order.
{ name: "A" },
{ name: "C" },
{ name: "A" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
let index = {};
let next = name => index[name] = index[name] + 1 || 0;
let result = array.map(obj => ({ ...obj, name: obj.name + '-' + next(obj.name) }));
console.log(result);

How can I detect duplicated items in an array of objects?

I want to find duplicated objects and add hasDuplicate: true property, but not the first one. Methods should be run after one element.
The example array
items: [
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
},
{
checked: false,
desc: "",
id: "230",
value: "Lorem"
},
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
hasDuplicate: true
},
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
hasDuplicate: true
},
{
checked: false,
desc: "",
id: "230",
value: "Lorem",
hasDuplicate: true
},
]
What is an efficient way to detect duplicate items in an array with ES6?
Use Array.prototype.map() to traverse your array and check whether the hash (Object.entries() concatenated) is already seen:
const src = [{checked:false,desc:"",id:"396",value:"Lorem",},{checked:false,desc:"",id:"230",value:"Lorem"},{checked:false,desc:"",id:"396",value:"Lorem"},{checked:false,desc:"",id:"396",value:"Lorem"},{desc:"",id:"230",checked:false,value:"Lorem"}],
dedupe = (a, hashMap=[]) => a.map(o => {
const hash = Object
.entries(o)
.sort(([a],[b]) =>
a.localeCompare(b))
.flat()
.join('\ud8ff')
return !hashMap.includes(hash) ?
(hashMap.push(hash), o) :
{...o, hasDuplicate: true}
})
console.log(dedupe(src))
.as-console-wrapper{min-height:100%;}
isIdentical() method will compare two objects and returns true if they are identical.
const isIdentical = (obj1, obj2) => {
let flag = true;
Object.keys(obj1).forEach(key => {
if(obj1[key] !== obj2[key]){
flag = false;
return false;
}
});
return flag;
};
Now reduce method will help us loop through the array.
items.reduce((unique, item) => {
const index = unique.findIndex( u => isIdentical(u, item));
if(index < 0){
return [...unique, item];
} else {
item.hasDuplicate = true;
return unique;
}
}, []);
console.log(items);
This sets the hasDuplicate property of every duplicate item except the first one based on their id
items.forEach((d, i) => {
if(i == items.map(d => d.id).indexOf(d.id)) {
d.hasDuplicate = true;
}
})

Finding matching objects in an array of objects?

var set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
I'd like to be able to do something like a db call, set.find({"color":"green"}) and have it return an array full of objects that contain that property.
Using Array#filter, for this particular case the code would look like
var results = set.filter(function (entry) { return entry.color === "green"; });
Array#filter is not implemented in some older browsers, so see the linked article for a backward compatibility shim, or better yet get a full-fledged ES5 shim.
For the more general case, it's just a matter of extending this idea:
function findByMatchingProperties(set, properties) {
return set.filter(function (entry) {
return Object.keys(properties).every(function (key) {
return entry[key] === properties[key];
});
});
}
var results = findByMatchingProperties(set, { color: "green" });
Again, I am using ECMAScript 5 methods Object.keys and Array#every, so use an ES5 shim. (The code is doable without an ES5 shim but uses manual loops and is much less fun to write and read.)
I have used map function from jquery and I am getting selected index by passing searched key value so by using that index we will get required object from array.
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
output
{ name: "Shyam", Id: 2 }
Note: if you want to pass search key as object then
searchKey = { Id: 2 };
mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey.Id)];
output
{ name: "Shyam", Id: 2 }
Using arrow functions with an implied return and concise body:
const results = set.filter(entry => entry.color === "green");
Another example passing in a search variable:
const searchString = 'green';
const results = set.filter(entry => entry.color === `${searchString}`);
Read more about arrow functions on
MDN
Since you've included the jQuery tag, here's one way to do it using jQuery's map:
var results = $.map( set, function(e,i){
if( e.color === 'green' ) return e;
});
The documentation states that you need to return null to remove the element from the array, but apparently this is false, as shown by the jsFiddle in the comments; returning nothing (i.e. returning undefined) works just as well.
I went with a different approach that I found to be a bit easier.
function isObjEqual(a, b) {
const x = JSON.stringify(a);
const y = JSON.stringify(b);
return x === y;
}
// Example 1
const set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
const findObj1 = {"color":"green"};
const arr1 = set.filter((objInArr) => isObjEqual(objInArr, findObj1));
console.log(arr1) // [ { color: 'green' }, { color: 'green' } ]
// Example 2
const list = [{
"label": "Option 2",
"value": "option2"
},
{
"label": "Option 3",
"value": "option3"
},
{
"label": "Option 2",
"value": "option2"
}
];
const findObj2 = {
"label": "Option 2",
"value": "option2"
}
const newList = list.filter((objInArr) => isObjEqual(objInArr, findObj2));
console.log(newList) //[ { label: 'Option 2', value: 'option2' }, { label: 'Option 2', value: 'option2' } ]

Categories

Resources