Is there any lodash function to achieve this? - javascript

I have an array
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
]
I need output like this:
[{id: 1, items: [3, 4, 5]}, {id: 2, items: [6,7]}, {id: 3, items: [8]}]

Here's a solution that first groups by id and then maps across the groupings to get the required collection:
let result = _(a)
.groupBy('id')
.map( (group ,id) => ({id: id, items: _.map(group, 'item')}))
.value()

It's pretty ugly, but then other answers are not pretty either
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
];
var ret = _.chain(a)
.groupBy(elt => elt.id)
.mapValues(elt => _.reduce(elt, (acc, sub) => acc.concat(sub.item),[]))
.map((value, key) => ({id: key, items:value}))
.value();
console.log(ret);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Related

how to map properties of two object arrays with different lengths in JavaScript

Being a newbie I have found this problem that I don't know to solve.
Given 2 arrays of different lengths I want to map the value of key total of arr2 over the value of key total of arr1 function of value of key grpId of arr1, as follow:
arr1 = [{id: 1, grpId: 'x', total: null},
{id: 2, grpId: 'x', total: null},
{id: 3, grpId: 'x', total: null},
{id: 4, grpId: 'y', total: null},
{id: 5, grpId: 'y', total: null}];
arr2 = [{id: 1, grpId: 'x', total: 3},
{id: 2, grpId: 'y', total: 2}];
I need the result as below:
arr1 = [{id: 1, grpId: 'x', total: 3},
{id: 2, grpId: 'x', total: 3},
{id: 3, grpId: 'x', total: 3},
{id: 4, grpId: 'y', total: 2},
{id: 5, grpId: 'y', total: 2}];
Try this:
const arr1 = [{id: 1, grpId: 'x', total: null},
{id: 2, grpId: 'x', total: null},
{id: 3, grpId: 'x', total: null},
{id: 4, grpId: 'y', total: null},
{id: 5, grpId: 'y', total: null}];
const arr2 = [{id: 1, grpId: 'x', total: 3},
{id: 2, grpId: 'y', total: 2}];
const output = arr1.map(entry => ({
...entry,
total: arr2.find(a2 => a2.grpId === entry.grpId).total
}));
console.log(output);
Should see the output in your console window. (Developer tools of your browser).
Use Array.map() to loop through arr1, and inside that loop through arr2 and assign the total values:
let arr1=[{id:1,grpId:"x",total:null},{id:2,grpId:"x",total:null},{id:3,grpId:"x",total:null},{id:4,grpId:"y",total:null},{id:5,grpId:"y",total:null}],arr2=[{id:1,grpId:"x",total:3},{id:2,grpId:"y",total:2}];
let res = arr1.map(el1 => {
arr2.forEach(el2 => {
if(el2.grpId == el1.grpId){
el1.total = el2.total
}
})
return el1
})
console.log(res)

finding index of item in nested array

If I have an array (sub) which has its own objects each with arrays within them and I'm looking for a particular value such as id === 9, how would I find the index of the object AND the index within that object's s array?
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id : 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.findIndex(a => a.s.findIndex(z => z.id === 9)))
If you're sure there's only one matching element in all your sub arrays, here's a little trick with flatMap.
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id: 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.flatMap((a, i) => {
const j = a.s.findIndex(z => z.id === 9);
return j > -1 ? [i, j] : []
}));
This will return an array containing the index, i, in a.sub where a matching element is found followed by the index, j, in a.sub[i].s where the matching element was found.
Note flatMap is a relatively recent addition to the standard, so it may not work in older browsers. Be sure to use a polyfill or a transpiler like Babel, if this is a concern in your case.
Try this:
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id : 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
v = 9
id1 = a.sub.findIndex(e => e.s.findIndex(ee => ee.id === v)!= -1)
id2 = a.sub[id1].s.findIndex(e => e.id === v )
console.log(id1) //index of the object
console.log(id2) //index within that object's s array
Modified answer of p.s.w.g, less likely to give you an eslint error.
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id: 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.flatMap((a, i) => {
const j = a.s.findIndex(z => z['id'] === 9);
return j > -1 ? [i, j] : []
}));

How can I merge two or more arrays containing several objects into one and increment their IDs?

I have three arrays of objects like so
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
and I want to merge them into a single array like this
const bigArray = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'},{id: 3, name: 'Pete'}, {id: 4, name: 'Liam'}, {id: 5, name: 'Baby'}, {id: 6, name: 'Neeson'}, {id: 7, name: 'Kobe'}];
I know I can spread them into a single array using the spread operator but I also need the IDs incremented when they are joined. How can I achieve that?
Reassign the id based on the index:
const merged = [...arr1, ...arr2, ...arr3];
merged.forEach((el, index) => el.id = index + 1);
Add all the array in a single array and than map over it and change index according to need.
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
let arr = [...arr1,...arr2,...arr3]
let op = arr.map((ele, index) => ({...ele, id: index+1}) )
console.log(op)
You can combine the arrays to a single array using rest parameters and Array.flat(), and then you can use Array.map() to update the objects with the id generated from the index:
const flatAndInc = (...args) =>
args.flat()
.map((o, idx) => ({
...o,
id: idx + 1
}));
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
const result = flatAndInc(arr1, arr2, arr3);
console.log(result);
You could map new objects without mutating the given data.
const
arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}],
arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}],
arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}],
result = [arr1, arr2, arr3].reduce(
(id => (r, a) => (a.forEach(({ name }) => r.push({ id: id++, name })), r))(1),
[]
);
console.log(result);
All of the other answers are valid, and are mostly more efficient and easier than mine. However, my answer is one of the easiest to understand.
My code uses Spread Syntax (...) to create the big array, but all that does is combines the arrays. Then, I use a .forEach() loop to loop through the array, and redefine the id. Then I incremented the id variable, so that the id's were in order.
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
let id = 1;
const bigArray = [...arr1, ...arr2, ...arr3];
bigArray.forEach(e => {
e.id = id;
id++;
});
console.log(bigArray);
You can use Array.from and spread
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
var result = Array.from([...arr1, ...arr2, ...arr3], ({name}, i)=>{ return {id: i + 1, name} });
console.log(result);
You can use reduce() map() and Spread Syntax.
Pass an array of all the arrays you want to merge to function
Flat that array using spread operator.
Then use reduce() to convert all the arrays to single dimensional array.
At last use map() and set it id property to index + 1
const arr1 = [{id: 1, name: 'Jay'}, {id: 2, name: 'Kay'}];
const arr2 = [{id: 1, name: 'Pete'}, {id: 2, name: 'Liam'}];
const arr3 = [{id: 1, name: 'Baby'}, {id: 2, name: 'Neeson'}, {id: 3, name: 'Kobe'}];
function merge(arrays){
return [...arrays].reduce((ac,a) => [...ac,...a],[]).map((x,i) => ({...x,id:i+1}));
}
console.log(merge([arr1,arr2,arr3]));

Ramda: get objects from array by comparing with each item in another array

I've an array like:
ids = [1,3,5];
and another array like:
items: [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
What I want is another array like:
array = [{id: 1, name: 'a'}, {id: 3, name: 'c'}, {id: 5, name: 'e'}];
I can't get my head around it. so far i tried like:
console.log(R.filter(R.propEq('id', <donnow what shud be here>), items);
console.log( R.pick(ids)(items))
If you still want to do with Ramda:
const ids = [1,3,5];
const items = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
console.log(
R.filter(R.compose(R.flip(R.contains)(ids), R.prop('id')), items)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
You can use .filter and .indexOf. Note these are ECMA5 methods for Arrays, and will not work in IE8.
var ids = [1, 3, 5];
var items = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
var filtered = items.filter(function(obj) {
return ids.indexOf(obj.id) > -1;
});
console.log(filtered); // [{id: 1, name: 'a'}, {id: 3, name: 'c'}, {id: 5, name: 'e'}];
Or may be one liner without Ramda
items.filter(x=>ids.includes(x.id))
I suggest to use a hash table for faster lookup.
var ids = [1, 3, 5],
items = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}, {id: 4, name: 'd'}, {id: 5, name: 'e'}, {id: 6, name: 'f'} ],
filtered = items.filter(function(obj) {
return this[obj.id];
}, ids.reduce(function (r, a) {
r[a] = true;
return r;
}, Object.create(null)));
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');

Compare the elements of two arrays by Id and remove the elements from the one array that are not presented in the other

I have two arrays of objects like this:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}]
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}]
I need to compare the elements of the two arrays by Id and remove the elements from arr1 that are not presented in arr2 ( does not have element with that Id). How can I do this ?
var res = arr1.filter(function(o) {
return arr2.some(function(o2) {
return o.Id === o2.Id;
})
});
shim, shim, shim.
You can use a function that accepts any number of arrays, and returns only the items that are present in all of them.
function compare() {
let arr = [...arguments];
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var arr3 = [{Id: 1, Name: "Test1"}, {Id: 6, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var new_arr = compare(arr1, arr2, arr3);
console.log(new_arr);
function compare() {
let arr = [...arguments]
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
Making use of a hash (a Set) will give a performance gain:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"},
{Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}];
arr1 = arr1.filter(function (el) {
return this.has(el.Id);
}, new Set(arr2.map(el => el.Id)));
console.log(arr1);
A new Set is created that gets the Id values from arr2:
"1","3"
That Set is passed as the thisArg to filter, so that within the filter callback it is available as this.

Categories

Resources