I have an array with objects
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
I want a new array [ 1, 2, 3, 1, 2, 3 ].
I have tried
nodes.map(node => node.children);
but it gives me [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
I have tried
[].concat(nodes.map(node => node.children));
but it doesn't work since it is just concatenating [] with [ [ 1, 2, 3 ], [ 1, 2, 3 ] ] which is just [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
You could use Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ],
result = nodes.reduce((r, node) => r.concat(node.children), []);
console.log(result);
console.log([... new Set(result)]); // for unique values
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do this with Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = nodes.reduce(function(r, o) {
r = r.concat(o.children);
return r;
}, []);
console.log(result)
Another way to do this using Array#forEach:
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ]
final = []
nodes.forEach(x => final = final.concat(x.children))
console.log(final)
Another shorter way is (a little modification to what OP was trying to do):
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = [].concat.apply([], nodes.map(x => x.children))
console.log(result);
Related
Let's say I have an array of id's
const ids = [[1, 2, 3], [2, 3], [3]]
And have array with objects that have name for each id
const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
What is the most proper way to get ids = [["One", "Two", "Three"], ["Two", "Three"], ["Three"]], I'm worrying that nested mapping could cause performance issues.
Use a combination of map and find. There may be a more performant way of doing it, but I'd suggest worrying about that only if you run into performance issues :-
const ids = [[1, 2, 3], [2, 3], [3]];
const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const mapped = ids.map(arr => arr.map(id => obj.find(obj => obj.id === id).name));
const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const ids = [[1, 2, 3], [2, 3], [3]] ;
// dedicated object to keep association between id and names
let names = {} ;
obj.forEach( o => {
names[ o.id ] = o.name ;
} ) ;
// map each sub array content to their real name
for( let i = 0; i <= ids.length-1; i++){
ids[i] = ids[i].map( id => names[id] ) ;
}
console.log( ids ) ;
=>
[ [ 'One', 'Two', 'Three' ], [ 'Two', 'Three' ], [ 'Three' ] ]
I have an array of objects (array1).
I want to filter every element, that includes ALL of the tags ([1, 2, 3).
So the result should be id 1. But i cannot make it work. The results i get are id 1, 2 ,4 and i do not understand why exactly it acts like this.
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
a1.tags.every((tag) => tags.includes(tag))
);
console.log(includesAll);
You should do the opposite. Instead of verifying that every value in a1.tags is also in tags, you'll want to verify that every value in tags is also in a1.tags:
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
console.log(includesAll);
Im looking to merge/combine objects in an array each with a series of nested arrays. I want to merge the objects based on a specific key (here label[1]). I can use Lodash and unionBy to filter out dublicates by label[1], but how do i keep the values from the filtered items?
The array can look like this:
var arr = [{
"label": ['item', 'private'],
"values": [1, 2, 3]
},
{
"label": ['item', 'private'],
"values": [1, 2, 3, 6]
},
{
"label": ['item', 'work'],
"values": [1, 2, 8, 9]
},
{
"label": ['item', 'private'],
"values": [1, 2, 4, 5]
},
{
"label": ['item', 'school'],
"values": [1, 2, 7]
}
];
And the desired output is:
var arr = [{
"label": ["item", "private"],
"values": [1, 2, 3, 4, 5, 6 ]
}, {
"label": ["item", "work"],
"values": [1, 2, 8, 9]
}, {
"label": ["item", "school"],
"values": [1, 2, 7]
}]
Here's a sample which is only half way there.
var arr = [
{ label: ['item','private'], values: [1,2,3] },
{ label: ['item','private'], values: [1,2,3,6] },
{ label: ['item','work'], values: [1,2,8,9] },
{ label: ['item','private'], values: [1,2,4,5] },
{ label: ['item','school'], values: [1,2,7] }
];
var result = _.unionBy(arr, "label[1]");
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Any ideas?
Thanks
Lasse
I'd write:
const arr2 = _(arr)
.groupBy(obj => obj.label[1])
.values()
.map(objs => ({
label: objs[0].label,
values: _(objs).flatMap("values").uniq().value(),
}))
.value()
Not sure how to do this with lodash but I don't think unionBy is the method to do this anyway.
Here is how you can group by label using lodash and then reduce the groups into one value to merge the items of a group.
const arr = [{"label":["item","private"],"values":[1,2,3]},{"label":["item","private"],"values":[1,2,3,6]},{"label":["item","work"],"values":[1,2,8,9]},{"label":["item","private"],"values":[1,2,4,5]},{"label":["item","school"],"values":[1,2,7]}];
console.log(
Object.values(
_.groupBy(arr, (item) => item.label.join()),//use lodash group by
).map((
group, //now we have array of array of groups
) =>
group
.reduce((result, item) => ({
//reduce a group to one object
label: result.label, //set label
values: [
//set values with unique values of all items
...new Set(
(result.values || []).concat(item.values || []),
),
],
})),
),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You could also do something like this via lodash:
var arr = [{ "label": ['item', 'private'], "values": [1, 2, 3] }, { "label": ['item', 'private'], "values": [1, 2, 3, 6] }, { "label": ['item', 'work'], "values": [1, 2, 8, 9] }, { "label": ['item', 'private'], "values": [1, 2, 4, 5] }, { "label": ['item', 'school'], "values": [1, 2, 7] } ]
const merge = arr => _.reduce(arr, (r,c) => _.union(r, c.values), [])
const result = _(arr).groupBy('label')
.entries()
.reduce((r,[k,v]) => (r.push({ label: k.split(','), values: merge(v) }), r), [])
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
The idea is first to group by via _.groupBy and then get the entries (via _.entries) so you can form the desired output via _.reduce. _.union we use to merge the values arrays as part of the final reduce.
Here is the ES6 implementation:
var arr = [{ "label": ['item', 'private'], "values": [1, 2, 3] }, { "label": ['item', 'private'], "values": [1, 2, 3, 6] }, { "label": ['item', 'work'], "values": [1, 2, 8, 9] }, { "label": ['item', 'private'], "values": [1, 2, 4, 5] }, { "label": ['item', 'school'], "values": [1, 2, 7] } ]
const mrg = arr => Array.from(new Set(arr.reduce((r,c) => [...r, ...c.values], [])))
const grp = (arr, k) => arr.reduce((r,c) => (r[c[k]] = [...r[c[k]] || [], c], r), {})
const result = Object.entries(grp(arr, 'label'))
.reduce((r,[k,v]) => (r.push({ label: k.split(','), values: mrg(v) }), r), [])
console.log(result)
I have an array of objects
[
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
]
I want to group these objects together with the other object with same values. So this array of objects should be grouped into 3 groups since the first 3 objects have the same values, the next object is alone and the last 2 objects have the same values.
As seen in the example, the values can have different orders, but should still be considered the same.
I am thinking about for each element to loop through the remaining elements and see if they are alike, but it will result in O(n^2).
I guess I should remove elements from the array that has already been grouped.
So how could I, efficiently, group it as
[[first, second, third],[fourth],[fifth,sixth]]
Something like this?
var data = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var hash = {};
for(var obj of data) {
var key = obj.values.sort().join("-");
if (!hash[key]) hash[key] = [];
hash[key].push(obj);
}
var result = [];
for(var k in hash) result.push(hash[k])
console.log(result)
Or js6 variant:
var data = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var hash = data.reduce((hash, obj) => {
const key = obj.values.sort().join("-");
if (!hash[key]) hash[key] = [];
hash[key].push(obj);
return hash;
}, [])
var result = Object.keys(hash).map(k => hash[k])
console.log(result)
You can do this with forEach() loop and sort()
var arr = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var result = [];
arr.forEach(function(e) {
var s = [].concat(e.values).sort().join('|');
if (!this[s]) {
this[s] = [e.values];
result.push(this[s]);
} else {
this[s].push(e.values)
}
})
console.log(result)
var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var itemList = [];
items.forEach(function(item) {
if (!_.isArray(item.item_name)) {
itemList.push({
name: item.item_name,
description: item.item_description,
quantity: item.item_quantity,
value: item.item_value
});
}
var names = item.item_name ? _.flatten(item.item_name) : [];
var descriptions = item.item_description ? _.flatten(item.item_description) : [];
var quantity = item.item_quantity ? _.flatten(item.item_quantity) : [];
var values = item.item_value ? _.flatten(item.item_value) : [];
names.forEach(function(name, index) {
itemList.push({
name: names[index],
description: descriptions[index],
quantity: quantity[index],
values: values[index]
});
})
});
console.log(itemList);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>
is there a way I can perform this faster in underscore, to remove all of the flattens?
for each item in the array I am taking
item_name[i]
item_description[i]
item_quantity[i]
item_value[i]
and adding them to the itemList
item properties in items can be [[],[]] or [] or integer or null
currently it is outputting what is expected (unless a name is null and it can skip items) however I do not like all of the loops this is performing and I am wondering if I can make a better use of underscore library
You can use this:
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
Demo:
var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>