How do I sort an array into subarrays - javascript

Please help with the task.
There is an array of objects:
[
{id: 0, title: 'title_1', step: 3},
{id: 1, title: 'title_2', step: 2},
{id: 2, title: 'title_3', step: 3},
{id: 3, title: 'title_4', step: 1},
{id: 4, title: 'title_5', step: 2},
...
]
How can I turn this array into this one?
[
[
{id: 0, title: 'title_1', step: 3},
{id: 2, title: 'title_3', step: 3},
],
[
{id: 3, title: 'title_4', step: 1},
],
[
{id: 1, title: 'title_2', step: 2},
{id: 4, title: 'title_5', step: 2},
],
...
]
That is, split it into subarrays of objects, using the step property
thanks

You could use Array.prototype.reduce() method to get the sub arrays by the step property. Traverse the array using reduce method and make step as a key as well as create sub array based on that key(step). At last, get all the values using Object.values() method.
const data = [
{ id: 0, title: 'title_1', step: 3 },
{ id: 1, title: 'title_2', step: 2 },
{ id: 2, title: 'title_3', step: 3 },
{ id: 3, title: 'title_4', step: 1 },
{ id: 4, title: 'title_5', step: 2 },
];
const ret = Object.values(
data.reduce((prev, c) => {
const p = prev;
const key = c.step;
p[key] = p[key] ?? [];
p[key].push(c);
return p;
}, {})
);
console.log(ret);

This version is algorithmically similar to the one from mr hr. It's just written in the mutation-free style I prefer. It is less performant, but to my mind easier to understand. But YMMV.
const groupArrayBy = (prop) => (arr) =>
Object .values (
arr .reduce ((a, x) => ({...a, [x [prop]]: [... (a [x [prop]] || []), x]}), {})
)
const input = [{id: 0, title: 'title_1', step: 3}, {id: 1, title: 'title_2', step: 2}, {id: 2, title: 'title_3', step: 3}, {id: 3, title: 'title_4', step: 1}, {id: 4, title: 'title_5', step: 2}]
console .log (groupArrayBy ('step') (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
We write a generic function groupArrayBy that takes a property name and returns a function that groups an array into subarrays sharing particular values for that property.
You can use it directly as we do above, or you can write a function that partially applies the first parameter, such as
const byStep = groupArrayBy ('step')
// ... later
const results = byStep (input)

Related

How to map through a deeply nested array of objects?

const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}],
{id: 2, arr: [{subId: 2, value: 2}],
{id: 3, arr: [{subId: 3, value: 1}],
]
how do I map over this array my_arr and then map over arr to return an array like so:
[
{subId: 1, value: 1},
{subId: 3, value: 1},
]
basically filtering out only where values are 1 and then returning only that sub object
I've tried doing
my_arr.map((x) => x.map((y) => y.value === 1 ? y : null))
You can try this approach with flatMap and filter
const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}]},
{id: 2, arr: [{subId: 2, value: 2}]},
{id: 3, arr: [{subId: 3, value: 1}]},
]
const result = my_arr.flatMap(item => item.arr).filter(item => item.value === 1)
console.log(result)
Your current approach maps over the outer array my_arr, and then uses an inner map to map over the inner array. Since .map() always returns an array, you'll end up mapping your objects from your outer array to other arrays, which you don't want. You can instead use .flatMap() which will combine/join the returned inner arrays into one array. However, rather than using .map() as your inner method though, you should use .filter() to create an array of objects that have a value of 1, which then gets merged into your resulting outer array created by the .flatMap() method:
const my_arr = [ {id: 1, arr: [{subId: 1, value: 1}]}, {id: 2, arr: [{subId: 2, value: 2}]}, {id: 3, arr: [{subId: 3, value: 1}]}, ];
const res = my_arr.flatMap(({arr}) => arr.filter(({value}) => value === 1));
console.log(res);
Since you are dealing with nested structure, you will have to get little creative.
First you will have to filter the array.
Inside it, you can use .some to check if your condition matches and return matching
Now you have the filtered list but you still need to format your output. You can use .reduce and concat arr of every item
This will be useful if you have multiple items in arr.
const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}] },
{id: 2, arr: [{subId: 2, value: 2}] },
{id: 3, arr: [{subId: 3, value: 1}] },
]
const output = my_arr
.filter(({ arr }) =>
arr.some(({value}) => value === 1)
).reduce((acc, { arr }) => acc.concat(arr), [])
console.log(output)

Summing Records and Consolidating Based on IDs in Array

I have an array of records that contain objects, each with an id and an amount. Now, in this array, some elements have the same id. What I need to do is iterate over this array, and first, sum up the values for records that have the same id, and then return just one record when that is the case - so I end up with an array with only unique ids -- each one also containing an amount property.
In other words, I want to take this array:
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
... and produce this array:
const transformedRecords = [
{id: 1, amount: 30},
{id: 2, amount: 10},
{id: 3, amount: 0}
];
I've thought about using a for-of loop for this, but that might bet pretty verbose, and I'm guessing there's a more succinct way to accomplish this - perhaps with reduce()? What's an elegant way to approach this, preferably using es6+ syntax?
Use Array.reduce, for each iteration, check if you have an object with the current id in the accumulator, if you do, add the amounts, if not, push the current object to the accumulator :
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10},
{id: 4, amount: -10},
{id: 4, amount: -10}
];
const result = records.reduce((acc, curr) => {
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) {
acc[ndx].amount += curr.amount
}
else{
acc.push(curr)
}
return acc;
}, [])
console.log(result)
You can use reduce() to create an object and then use map() on its entries to create array of objects back
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
const res = Object.entries(records.reduce((ac, a) => {
ac[a.id] = (a[a.id] || 0) + a.amount;
return ac
}, {})).map(([id, amount]) => ({id, amount: amount < 0 ? 0 : amount}))
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] : []
}));

Is there any lodash function to achieve this?

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>

Underscore - Compare two arrays of objects (positions)

Is there a way to compare differences between arrays based on changes on their elements positions?
I have an original array of objects which undergoes a change on one of it's element's values, this change is mapped into a new array:
origElements = [{id: 1, value: 50},
{id: 2, value: 60},
{id: 3, value: 70}]
changedElements = [{id: 1, value: 50},
{id: 3, value: 60},
{id: 2, value: 120}]
var diff = _.difference(_.pluck(origElements, "id"), _.pluck(changedElements, "id"));
var result = _.filter(origElements, function(obj) { return diff.indexOf(obj.id) >= 0; });
In this case it is clear why 'result' would return nothing. As there's no difference of values between: [1, 2, 3] and [1, 3, 2]. What I'm trying to achieve here is a 'strict difference' which would look at index as well, thus returning some reference to the new order of the objects.
How about doing it this way:
var origElements = [{
id: 1,
value: 50
}, {
id: 2,
value: 60
}, {
id: 3,
value: 70
}];
var changedElements = [{
id: 1,
value: 50
}, {
id: 3,
value: 60
}, {
id: 2,
value: 120
}];
var origElementsIds = _.pluck(origElements, "id");
var changedElementsIds = _.pluck(changedElements, "id");
console.log("Are array element positions same ?",
origElementsIds.join() === changedElementsIds.join());

Categories

Resources