I have an array that looks like this:
[{location: {…}, distance: 0}
{location: {…}, distance: 0}
{location: {…}, distance: 0.37348441209694133}
{location: {…}, distance: 0}
{location: {…}, distance: 0.4229382782128456}
{location: {…}, distance: 0}
{location: {…}, distance: 0.006098292961396555}
{location: {…}, distance: 0}
{location: {…}, distance: 0.07885846317546494}]
I want to map a new array with the distance values incremented from the previous value. So in the example above the last distance value would be 0.88137944644665 because it was have added all the values by the time it iterated all objects.
In Ramda.js I've tried to add the previous distance value when I map the array but it doesn't work because it's undefined. I've also looked into reduce with no success. Any ideas on how I can accomplish this in Ramda.js?
Much as I love Ramda you don't need it here:
arr.map(({ location, distance }) => ({ location, distance: distance + 1 }));
To break that down:
arr // your array of objects
.map( // call to Array.prototype.map
({ location, distance }, i, arr) => // function params, destructures
({ location, distance: distance + 1 })); // return new object same loc, incremented distance
EDIT
Since I missed the part about aggregation, use reduce instead:
arr.reduce((aggregate, { location, distance }, i) => {
const dist = i ? aggregate[i - 1].distance : 0;
aggregate.push({ location, distance: distance + dist });
return aggregate;
}, []);
Try the following
const reducedData = data.reduce( (prev, val) => {
prev.sum += val.distance
prev.datas.push({
...val,
distance: prev.sum
})
return prev;
}, {sum: 0, datas: []})
The .reduce loops on the data and merges each row into prev. The final output of this script is an object that looks like { sum: number , datas: [...]}, sum in here is the total distance, and the datas will contain the array that you want.
See snapshot:
If you were looking utilise Ramda functions here, R.scan can help. This function is very similar to reduce though instead of returning a single summarised value it will produce a list of every successive result.
const distanceL = R.lensProp('distance')
const addDistance = R.compose(R.add, R.prop('distance'))
const fn = data =>
R.scan(
(acc, next) => R.over(distanceL, addDistance(acc), next),
data[0],
R.tail(data)
)
console.log(fn([
{location: {}, distance: 0},
{location: {}, distance: 0},
{location: {}, distance: 0.37348441209694133},
{location: {}, distance: 0},
{location: {}, distance: 0.4229382782128456},
{location: {}, distance: 0},
{location: {}, distance: 0.006098292961396555},
{location: {}, distance: 0},
{location: {}, distance: 0.07885846317546494}
]))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Or you could use scan:
const {lift, scan, head, tail} = R
const xs = [
{location: {id: 1}, distance: 0},
{location: {id: 2}, distance: 0},
{location: {id: 3}, distance: 0.37348441209694133},
{location: {id: 4}, distance: 0},
{location: {id: 5}, distance: 0.4229382782128456},
{location: {id: 6}, distance: 0},
{location: {id: 7}, distance: 0.006098292961396555},
{location: {id: 8}, distance: 0},
{location: {id: 9}, distance: 0.07885846317546494}
]
const accumDist = lift(scan((agg, x) => ({...x, distance: x.distance + agg.distance})))(head, tail)
console.log(accumDist(xs))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
This won't work with an empty list. If that's a concern, we could alter it by skipping lift(...)(head, tail) and just using the scan with an initial value, and then taking the tail. But if that's not a concern, this code is clearer.
Related
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)
I have this array shown in console
(3) [{…}, {…}, {…}]
0: {num3: 1, num2: 1}
1: {num3: 2, num2: 4}
2: {num3: 3, num2: 1}
length: 3
pop: ƒ ()
push: ƒ ()
shift: ƒ ()
splice: ƒ ()
unshift: ƒ ()
_chartjs: {listeners: Array(1)}
__proto__: Array(0)
I just want to change the format to this
(3) [{…}, {…}, {…}]
0: {1, 1}
1: {2, 4}
2: {3, 1}
length: 3
pop: ƒ ()
push: ƒ ()
shift: ƒ ()
splice: ƒ ()
unshift: ƒ ()
_chartjs: {listeners: Array(1)}
__proto__: Array(0)
So then I can use it to draw a scatter chart using chart.js
You can use map function like so.
let newArr = oldArr.map(x => [x.num3, x.num2])
I think you mean to output an array of arrays considering an object must have a key/value. To do so, this function should get the job done.
const formatArr = (arr) => arr.map(({ num1, num2 }) => ([num1, num2]))
You can then use this for multiple charts like so:
const newChart = formatArr(oldChart)
FYI: You should probably rename my function to something more descriptive.
Since you want to remove the name, then you should turn to an object into an array of values.
you cant do this: item = {1,2} but you can do this item = [1,2].
Considering you meant removing the properties which will lead to an array [1,2], Try this:
const arr = [
{num3: 1, num2: 1},
{num3: 2, num2: 4},
{num3: 3, num2: 1}]
const arr2 = arr.map(k => Object.keys(k).map(ok => k[ok]))
console.log({arr2})
Update: Looking at the scatter chart in chart.js you should have an x and a y as props instead, use this:
const arr = [
{num3: 1, num2: 1},
{num3: 2, num2: 4},
{num3: 3, num2: 1}]
// your object should be like this
const data = arr.map(k => ({x: k.num3,y: k.num2}))
console.log(data)
Reference: Scatter Chart - Chart.js
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)
Is it possible in some way to filter let's say such an array of object arrays:
[[{id: 1}, {id: 2}, {id: 3}], [{id:6}, {id: 2}], [{id: 2}, {id: 1}, {id: 9}, {id: 3}]]
To get array of objects which all arrays have the same property (id), so in this case it output will be:
[{id: 2}] // becasue this id is the same in all three subarrays
I've only try intersectionBy from loadash but it seems to work in completely other way :/
I would take one array (it's enough to take one because if the property is not there its already not common), in this example I'm taking the first one but probably more efficient will be picking the shortest one.
iterate over the array and check for each object if its common to all other arrays.
const arr = [[{id: 1}, {id: 2}, {id: 3}], [{id:6}, {id: 2}], [{id: 2}, {id: 1}, {id: 9}, {id: 3}]];
let firstArray = arr.shift();
const result = firstArray.reduce((common, item)=>{
if (arr.every(inner => inner.some(_item => _item.id === item.id))) {
common.push(item);
}
return common;
},[])
console.log(result);
Using Ramda:
const input = [[{id: 1}, {id: 2}, {id: 3}], [{id:6}, {id: 2}], [{id: 2}, {id: 1}, {id: 9}, {id: 3}]];
R.intersection(...input);
You can use array reduce,forEach , findIndex and sort to get the most common object. In first inside the reduce callback use forEach to iterate each of the child array and then use findIndex to find if in accumulator array , there exist an object with same id. If it does not exist create a new object with key id & occurrence. If it exist then increase the value of occurrence. This will give the most common id, even if an id is not present in few child array
let data = [
[{id: 1}, {id: 2}, { id: 3}],
[{id: 6}, {id: 2}],
[{id: 2}, {id: 1}, {id: 9}, { id: 3}]
];
let obj = data.reduce((acc, curr) => {
curr.forEach((item) => {
let getInd = acc.findIndex((elem) => {
return elem.id === item.id
});
if (getInd === -1) {
acc.push({
id: item.id,
occurence: 1
})
} else {
acc[getInd].occurence += 1;
}
})
return acc;
}, []).sort((a, b) => {
return b.occurence - a.occurence;
});
console.log(obj[0])
var arr = [
[{id: 1}, {id: 2}, {id: 3}],
[{id:6}, {id: 2}],
[{id: 2}, {id: 1}, {id: 9}, {id: 3}]
]
var obj = {};
var arrLength = arr.length;
arr.forEach((val,index) => {
val.forEach((item) =>{
if(index == 0){
if(!obj.hasOwnProperty(item.id)){
obj[item.id] = 1;
}
}else{
if(obj.hasOwnProperty(item.id)){
obj[item.id] = obj[item.id] + 1;
}else{
return;
}
}
});
});
var output = [];
for (const property in obj) {
if(obj[property] == arrLength){
output.push({
id: property
})
}
}
console.log(output);
My approach is similar to that of naortor, but with an attempt to be more generic.
const intersection = (pred) => (as, bs) =>
as .filter (a => bs .some (b => pred (a, b)))
const intersectionAll = (pred) => (xs) =>
xs.length ? xs .reduce (intersection (pred)) : []
const input = [[{id: 1}, {id: 2}, {id: 3}], [{id:6}, {id: 2}], [{id: 2}, {id: 1}, {id: 9}, {id: 3}]]
const eqIds = (a, b) => a .id == b .id
console .log (
intersectionAll (eqIds) (input)
)
.as-console-wrapper {min-height: 100% !important}
This version requires you to say how you identify two equal values. (We will check that they have the same id, but any binary predicate function is allowed.) This function is passed to intersection which returns a function that takes two arrays and finds all the element in common between those two. intersectionAll wraps this behavior up, folding intersection over an array of arrays.
This breakdown is useful, as intersection is a useful function on its own too. And abstracting out the id check into a function you need to supply means these functions are much more generic.
Like this is my array of objects:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
Now I want to achieve all total sum of similar object keys like this
[
{_id: 1, total: 47},
{_id: 2, total: 36},
{_id: 3, total: 25},
{_id: 4, total: 79},
]
Can anyone suggest how to do this on es6
Use reduce. reduce is an array method that can transform an array into something else, i.e. another array that can have different length. map will always return an array with the same number of elements. And filter can return an array that has less elements but the elements will be unchanged.
Reduce gives you the more flexible behavior. You can change the elements and you can store them in whatever way you like.
const result = x.reduce((acc, el) => {
const index = acc.findIndex(({_id}) => el._id === _id);
if (index > -1) {
acc[index].total += el.total;
} else {
acc.push({...el});
}
return acc;
}, [])
console.log(result);
In case if this code would run often and on large arrays, you can use more performant but a little more complex solution where we use a hash table to store the data:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
const temp = {};
for (const el of x) {
temp[el._id] = (temp[el._id] || 0) + el.total;
}
const result = Object.entries(temp).map(([_id, total]) => ({_id, total}));
console.log(result);
But before starting optimizing you should always check if it's worth doing by running perf tools.