Hi i want to sort an array of objects in javascript. Below is the example data.
const example = [
{
name: "c_name",
children: [{
name: "child",
email: "child1#dev.com",
children: [{
name: "nested_child",
email: "nestedchild1#dev.com",
}]
}]
},
{
name: "a_name",
children: [{
name: "some_name",
email: "some_name#dev.com",
children: []
}]
},
{
name: "name",
children: [{
name: "child_name",
email: "child_name#dev.com",
children: []
}]
}
];
Should sort this array based on property 'name' and the children object should be sorted again based on 'name' property.
So the expected output is like below, and would like to retain other properties as well like email property in children.
a_name
some_name
c_name
child
nested_child
name
child_name
What i have done...i have a sort function that sorts the array by name property. however dont know how to sort the children object with name property.
const sorted_example = example.sort(this.sort_by_name());
sort_by_name = () => {
return (a, b) => {
let result;
const a_value = a.name.toLowerCase();
const b_value = b.name.toLowerCase();
if (a_value > b_value) {
result = 1;
} else if (a_value < b_value) {
result = -1;
} else {
result = 0;
}
return result;
};
};
Could someone help me how to continue with this. thanks.
The previous answers got you there most of the way but you need to sort again if an item has children. In my example I don't mutate the original array (use .slice to make a shallow copy so .sort doesn't mutate).
const example = [{"name":"c_name","children":[{"name":"child","email":"child1#dev.com","children":[{"name":"nested_child","email":"nestedchild1#dev.com"}]},{"name":"b"},{"name":"a"}]},{"name":"a_name","children":[{"name":"some_name","email":"some_name#dev.com","children":[]}]},{"name":"name","children":[{"name":"child_name","email":"child_name#dev.com","children":[]}]}];
const sortRecursive = (data) => {
const recur = (arr) =>
arr
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
//check each item to see if it has children that is an array
.map(
(item) =>
//if item has children that is an array then sort that
// and it's childrens childrens children
Array.isArray(item.children)
? {
...item,
children: recur(item.children),
}
: item, //no children, just return the item
);
return recur(data);
};
//note that sortRecursive does not change example but returns a new array
// that is sorted
console.log(sortRecursive(example));
assuming your children are arrays instead of objects as per your example:
const example = [
{
name: "c_name",
children: [{
name: "child",
email: "child1#dev.com",
children: [{
name: "nested_child",
email: "nestedchild1#dev.com",
}]
}]
},
{
name: "a_name",
children: [{
name: "some_name",
email: "some_name#dev.com",
children: []
}]
},
{
name: "name",
children: [{
name: "child_name",
email: "child_name#dev.com",
children: []
}]
}
];
a quick way would be:
example
.sort((a, b) => a.name.localeCompare(b.name))
.map(m => {
return {
name: m.name,
children: m.children.sort((a, b) => a.name.localeCompare(b.name))
};
});
You can simply use the sort() method
example.sort((el, q) => el.name.localeCompare(q.name))
Related
I have an array like this
const arr = [{ name: 'sara' }, { name: 'joe' }];
and i want to check if name is unique across the array so
const arr = [{ name: 'sara' }, { name: 'joe' }];//sara is unique //true
const arr = [{ name: 'sara' }, { name: 'sara' },{ name: 'joe' }];//sara is unique //false
i know there's array.some but it doesn't help in my situation
whats is the best way to achieve that using javascript thanks in advance
You could take a single loop and a Set for seen value.
isUnique is a function which takes a property name and returns a function for Array#every.
const
isUnique = (key, s = new Set) => o => !s.has(o[key]) && s.add(o[key]),
a = [{ name: 'sara' }, { name: 'joe' }],
b = [{ name: 'sara' }, { name: 'sara' }, { name: 'joe' }];
console.log(a.every(isUnique('name'))); // true
console.log(b.every(isUnique('name'))); // false
i have this implementation and it dose the job
const arr = [{ name: "salah" }, { name: "joe" }];
let bols = [];
arr.forEach((item1, i1) => {
arr.forEach((item2, i2) => {
if (i1 !== i2) {
bols.push(item2.name === item1.name);
}
});
});
bols.some((item) => item === true);
I have an array of objects,
c = [
{
name: 'abc',
category: 'cat1',
profitc: 'profit1',
costc: 'cost1'
},
{
name: 'xyz',
category: '',
profitc: 'profit1',
costc: ''
},
{
name: 'pqr',
category: 'cat1',
profitc: 'profit1',
costc: ''
}
]
Now I want to filter array based on another array of objects, the second array of objects is,
arr = [
{
type:'profitc'
value: 'profit1',
},
{
type:'category'
value: 'cat1',
}
]
Now the arr is shown in dropdown with multiple select option and the value of key value in the object is shown to the user i.e profit1, cat1, etc. So if a user selects profit1 and cat1, then I need to filter the array c such that, the output looks like this.
c = [
{
name: 'abc',
category: 'cat1',
profitc: 'profit1',
costc: 'cost1'
},
{
name: 'pqr',
category: 'cat1',
profitc: 'profit1',
costc: ''
}
]
I tried doing this.
let result = c.filter(e => {
let istruecat = true
//arr is chosen value from user.
arr.forEach(element => {
istruecat = e[element.type] == element.value;
})
return istruecat;
})
But when I do this I get all the objects from the c array. What am I doing wrong here? Is there a an way to do this using lodash.
-You compute istruecat based only on the last entry in arr. You should use reduce instead to accumulate value:
let result = c.filter(e => arr.reduce((acc, element) => acc && e[element.type] === element.value, true))
You could filter the array by checking all given key/values pairs with the data's objects.
var data = [{ name: 'abc', category: 'cat1', profitc: 'profit1', costc: 'cost1' }, { name: 'xyz', category: '', profitc: 'profit1', costc: '' }, { name: 'pqr', category: 'cat1', profitc: 'profit1', costc: '' }],
filters = [{ type: 'profitc', value: 'profit1', }, { type: 'category', value: 'cat1' }],
result = data.filter(o => filters.every(({ type, value }) => o[type] === value));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an implementation of reducing the list c based in the given values by the filter array arr. Note that the output is a new list based on the initial content in c.
result = arr.reduce((acc, curr) => {
acc = acc.filter(item => {
return item[curr.type] === curr.value;
});
return acc;
}, c);
Or a recursive and imo readable solution:
function myFilter(c, [first, ...rest]) {
if (first) {
const {type, value} = first;
// Recursively call myFilter with one filter removed
return myFilter(c, rest)
.filter(x => x[type] === value);
} else {
// Nothing left to filter
return c;
}
}
myFilter(c, arr);
I have an array of objects and I would like to convert it into a different array. Each object in the original array has a category key and I would like the final result to group objects by category. I am trying to use the reduce method to do this but can not make sense of the examples I have found.
original array:
[
{category: "film", title: "toy story"},
{category: "film", title:"harry potter"},
{category: "tv", title:"seinfeld"}
]
desired result:
[
{
category: "film",
children: [
{title: "toy story"},
{title: "harry potter"}
],
}
{
category: "tv",
children: [
{title: 'seinfeld' }
]
}
]
I am trying to use d3 to create some graphs and the data needs to be sorted in a hierarchical structure. More on that here, https://github.com/d3/d3-hierarchy/blob/v1.1.9/README.md#hierarchy
Use reduce function,
first try to look if the item already exists in the array if yes, use the found index to locate the element and append to its children, if not add the initial item to the children array.
const input = [{
category: "film",
title: "toy story"
},
{
category: "film",
title: "harry potter"
},
{
category: "tv",
title: "seinfeld"
}
]
const result = input.reduce((acc, x) => {
const index = acc.findIndex(y => y.category === x.category)
if (index >= 0) {
acc[index].children.push({
title: x.title
})
} else {
acc = [...acc, {
category: x.category,
children: [{
title: x.title
}]
}]
}
return acc;
}, []);
console.log(result)
For not having child duplicates:
const result = input.reduce((acc, x) => {
const index = acc.findIndex(y => y.category === x.category)
const indexTitle = acc[index] && acc[index].children.findIndex(y => y.title === x.title)
if (index >= 0) {
if (indexTitle !== 0) {
acc[index].children.push({
title: x.title
})
}
} else {
acc = [...acc, {
category: x.category,
children: [{
title: x.title
}]
}]
}
return acc;
}, []);
You can use a reduce function:
const arr = [
{category: "film", title: "toy story"},
{category: "film", title: "harry potter"},
{category: "tv", title: "seinfeld"}
]
const arrByCategory = arr.reduce((acc, i) => {
// Check if the category already exist in the new array
const elIdx = acc.findIndex(_ => _.category === i.category);
const elExist = elIdx > -1;
if(elExist) {
// If the category exist, we just add the title to the children list
return acc[elIdx].children.push({title: i.title})
} else {
// If the category does not exist we create it and add the initial title in the children list
return acc.concat({
category: i.category,
children: [{ title: i.title }]
})
}
},[])
To give you a better understanding of the reduce function, here is a simple example:
const array = [1, 3, 6, 2, 5]
const sum = array.reduce((acc, i) => {
return acc + i;
}, 0)
console.log(sum) // 17
I would like to fuse Array.filter() function to remove duplicate objects
I am able to achieve in the case of string or integer arrays. But I am not able to achieve the same with array of objects as in the second case of names
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
let x = names => names.filter((v, i, arr) => arr.indexOf(v) === i);
console.log(x(names)); //[ 'John', 'Paul', 'George', 'Ringo' ]
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
// returns the same original array
Could you please help?
Using Array#reduce() and a Map accumulator then spread the values() of the Map into array
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
const unique = [... names.reduce((a,c)=>(a.set(c.name,c)),new Map).values()]
console.log(unique)
Use Array.reduce and Object.values
Iterate over the array and create an object with key as name and value as object from array. In case of objects with same name, the value will be overwritten in resultant object. Finally use Object.values to collect all the unique objects.
const names = [{ name: "John" },{ name: "Paul" },{ name: "George" },{ name: "Ringo" },{ name: "John" } ];
let result = Object.values(names.reduce((a,c) => Object.assign(a, {[c.name]:c}),{}));
console.log(result);
For tweaking - Plunker
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" }
];
/* unique => Filter: Remove all duplicate items from an array. Works with plain objects as well, since we stringify each array item.
* #type public Function
* #name unique
* #return Function( item )
* #notes
*/
const unique = () => {
const seen = {};
return item => {
const json = JSON.stringify( item );
return seen.hasOwnProperty( json )
? false
: ( seen[ json ] = true );
};
};
const result = names.filter( unique() );
console.log( result );
You could use lodash's _uniqBy for this:
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
const result = _uniqBy(names, 'name');
This can be done with the help of Sets as well
var names = [{ name: "John" },{ name: "Paul" },{ name: "George" },{ name: "Ringo" },{ name: "John" } ];
var result = Array.from(
names.reduce((s, d) => s.add(d.name), new Set)
, d => ({ name: d })
)
console.log(result)
Keith had a great suggestion to use findIndex with filter instead of indexOf. Object literals are always unique references, so we cannot compare them. We can however compare the name keys between the objects. We can do this with the aforementioned functions.
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" }
];
console.log(names.filter(({name1}, i, a) => {
return i == a.findIndex(({name2}) => {
return name1 == name2;
});
});
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
function removeDups(names) {
let unique = {};
names.forEach(function(i) {
if(!unique[i]) {
unique[i] = true;
}
});
return Object.keys(unique);
}
removeDups(names); //'John', 'Paul', 'George', 'Ringo'
I am trying to get duplicate objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' },
{ id: 11, name: 'someName3' },
{ id: 12, name: 'someName4' }
];
Duplicate objects should return like below:
duplicate = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' }
];
You can use Array#reduce to make a counter lookup table based on the id key, then use Array#filter to remove any items that appeared only once in the lookup table. Time complexity is O(n).
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const lookup = values.reduce((a, e) => {
a[e.id] = ++a[e.id] || 0;
return a;
}, {});
console.log(values.filter(e => lookup[e.id]));
Let's say you have:
arr = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
So, to get unique items:
unique = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) === i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]);
Then, result will be
unique = [
{ id:10, name: 'someName1' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
And, to get duplicate ids:
duplicateIds = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) !== i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]["id"])
List of IDs will be
duplicateIds = [10]
Thus, to get duplicates objects:
duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));
Now you have it:
duplicate = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' }
]
Thanks https://reactgo.com/removeduplicateobjects/
You haven't clarified whether two objects with different ids, but the same "name" count as a duplicate. I will assume those do not count as a duplicate; in other words, only objects with the same id will count as duplicate.
let ids = {};
let dups = [];
values.forEach((val)=> {
if (ids[val.id]) {
// we have already found this same id
dups.push(val)
} else {
ids[val.id] = true;
}
})
return dups;
With lodash you can solve this with filter and countBy for complexity of O(n):
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could do the same with ES6 like so:
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))
You can use an array to store unique elements and use filter on values to only return duplicates.
const unique = []
const duplicates = values.filter(o => {
if(unique.find(i => i.id === o.id && i.name === o.name)) {
return true
}
unique.push(o)
return false;
})
With lodash you can use _.groupBy() to group elements by their id. Than _.filter() out groups that have less than two members, and _.flatten() the results:
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const result = _.flow([
arr => _.groupBy(arr, 'id'), // group elements by id
g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
_.flatten // flatten the results to a single array
])(values);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
An alternative based in #ggorlen solution with new Map() as accumulator (for better performance) and without unary operator ++ (not advised by default in projects with ESLint).
const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];
const lookup = values.reduce((a, e) => {
a.set(e.id, (a.get(e.id) ?? 0) + 1);
return a;
}, new Map());
console.log(values.filter(e => lookup.get(e.id) > 1));
Try this
function checkDuplicateInObject(propertyName, inputArray) {
var seenDuplicate = false,
testObject = {};
inputArray.map(function(item) {
var itemPropertyName = item[propertyName];
if (itemPropertyName in testObject) {
testObject[itemPropertyName].duplicate = true;
item.duplicate = true;
seenDuplicate = true;
}
else {
testObject[itemPropertyName] = item;
delete item.duplicate;
}
});
return seenDuplicate;
}
referred from : http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/