Grouping and hierarchical mapping with lodash - javascript

I'm trying to transform some data by using Lodash groupBy and map. Here is sample data:
var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]
I need that data in the format:
var data = [
{name: 'x', pricing: [{qty: 0, rate: 10}, {qty: 10, rate: 2}]},
{name: 'y', pricing: [{qty: 5, rate: 20}, {qty: 55, rate: 11}]}]
The following is my attempt:
var m = _.chain(data)
.groupBy(data, 'name')
.map( function(i) {
return {
name: _.first(i).name,
pricing: _.map(i, function(r) {
return _.pick(r, ['qty', 'rate'])
})
}
})
This produces
[{
"name": "x",
"pricing": [
{"qty": 0, "rate": 10},
{"qty": 10, "rate": 2},
{"qty": 5,"rate": 20},
{"qty": 55,"rate": 11}]
}]
I've been unable to figure out what I'm doing wrong. Maybe this isn't even valid and there is a better way?

You need to map new object and get the picked values.
var data = [{ name: 'x', qty: 0, rate: 10 }, { name: 'x', qty: 10, rate: 2 }, { name: 'y', qty: 5, rate: 20 }, { name: 'y', qty: 55, rate: 11 }],
result = _(data)
.groupBy('name')
.map((pricing, name) => ({
name,
pricing: _.map(pricing, _.partialRight(_.pick, ['qty', 'rate']))
}))
.value();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

You don't need to use lodash for this - standard Javascript array methods work just fine. Reduce into an object indexed by name, then get that object's values:
var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]
const obj = data.reduce((a, { name, ...rest }) => {
if (!a[name]) a[name] = { name, pricing: [] };
a[name].pricing.push(rest);
return a;
}, {});
const output = Object.values(obj);
console.log(output);

You can do that using reduce()
var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]
let res = data.reduce((ac,a) => {
let i = ac.findIndex(x => x.name === a.name);
if(i === -1) i = ac.push({name:a.name,pricing:[]}) - 1
ac[i].pricing.push({qty:a.qty,rate:a.rate});
return ac;
},[])
console.log(res);

Your code is actually fine, except for a small mistake. You start a chain with data, and then you try to groupBy the data. Since non of the items return true for this predicate, all are bundled under a single group, and this is the reason for your single object.
You need to change .groupBy(data, 'name') to .groupBy('name').
Example:
var data = [{ name: 'x', qty: 0, rate: 10 }, { name: 'x', qty: 10, rate: 2 }, { name: 'y', qty: 5, rate: 20 }, { name: 'y', qty: 55, rate: 11 }];
var m = _.chain(data)
.groupBy('name') // group by name, and not by data, 'name'
.map(function(i) {
return {
name: _.first(i).name,
pricing: _.map(i, function(r) {
return _.pick(r, ['qty', 'rate'])
})
}
})
console.log(m);
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Related

JavaScript: Comparing arrays of objects with nested array of object

I would like to compare the var arrayB with var arrayA in the said condition as arrayA[n].[m].id will be matched with arrayB[ele].optionValue[e].id.
var arrayA = [
[{value: "#0767b9", id: 162,productId: 1}, value: "#f4b7d4",id: 164,productId: 1],
[{value: "#44acd8",id: 102,productId: 2}],
[{value: "#609923",id: 106,productId: 3}, {value: "#ee3b70",id: 107,productId: 3}]
]
var arrayB = [
{
id: 1,
optionValue: [{value: "#002e63",id: 161,productId: 1}, {value: "#0767b9",id: 162,productId: 1},{value: "#010b1d",id: 163,productId: 1}, {value: "#f4b7d4",id: 164,productId: 1}]
},
{
id: 2,
optionValue: [{value: "#EC7063",id: 93,productId: 2}, {value: "#bf0000",id: 94,productId: 2}, {value: "#44acd8",id: 102,productId: 2}, {value: "#ffdbdb",id: 103,productId: 2}]
},
{
id: 3,
optionValue: [{value: "#d861bd",id: 105,productId: 3}, {value: "#609923",id: 106,productId: 3}, {value: "#ee3b70",id: 107,productId: 3}]
},
{
id: 4,
optionValue: [{value: "#44acd8",id: 165,productId: 4}]
}
]
My goal is to return var arrayB with that filtered data that will remove the object which is not in the var arrayA, like this:
var result = [
{
id: 1,
optionValue: [{value: "#0767b9",id: 162,productId: 1},{value: "#f4b7d4",id: 164,productId: 1}]
},
{
id: 2,
optionValue: [{value: "#44acd8",id: 102,productId: 2}]
},
{
id: 3,
optionValue: [{value: "#609923",id: 106,productId: 3},{value: "#ee3b70",id: 107,productId: 3}]
},
{
id: 4,
optionValue: [{value: "#44acd8",id: 165,productId: 4}]
}
]
I have workaround as below but that is not giving the desired output.
const myArray = arrayB.map((el) => {
el.optionValue.filter((fl) => {
arrayA.map(values => {
values.map((value) => {
!value.id.includes(fl.id)
})
})
})
});
Note: For id:4 in result set is the case that is the selected productId for which there is no any value is selected. So in arrayA there is no value for productId:4. So in result for this kind of cases if there is no values are for comparison then it should be return as it is instead of blank array.
If you like to get only common identifier pairs from both, you could collect the identifier and map the filterd array of arrayB.
This approach takes only one loop for every array.
const
arrayA = [[{ value: "#0767b9", id: 162, productId: 1 }, { value: "#f4b7d4", id: 164, productId: 1 }], [{ value: "#44acd8", id: 102, productId: 2 }], [{ value: "#609923", id: 106, productId: 3 }, { value: "#ee3b70", id: 107, productId: 3 }]],
arrayB = [{ id: 1, optionValue: [{ value: "#002e63", id: 161, productId: 1 }, { value: "#0767b9", id: 162, productId: 1 }, { value: "#010b1d", id: 163, productId: 1 }, { value: "#f4b7d4", id: 164, productId: 1 }] }, { id: 2, optionValue: [{ value: "#EC7063", id: 93, productId: 2 }, { value: "#bf0000", id: 94, productId: 2 }, { value: "#44acd8", id: 102, productId: 2 }, { value: "#ffdbdb", id: 103, productId: 2 }] }, { id: 3, optionValue: [{ value: "#d861bd", id: 105, productId: 3 }, { value: "#609923", id: 106, productId: 3 }, { value: "#ee3b70", id: 107, productId: 3 }] }, { id: 4, optionValue: [{ value: "#44acd8", id: 165, productId: 4 }] }],
identifiers = arrayA.reduce((r, a) => {
a.forEach(({ id, productId }) => (r[productId] = r[productId] || {})[id] = true);
return r;
}, {}),
result = arrayB.map(o => identifiers[o.id]
? { ...o, optionValue: o.optionValue.filter(({ id, productId }) => identifiers[productId][id]) }
: o
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Can try out with this:
var arrayA = [
[{value: "#0767b9", id: 162, productId: 1}, {value: "#f4b7d4", id: 164, productId: 1}],
[{value: "#44acd8", id: 102, productId: 2}],
[{value: "#609923", id: 106, productId: 3}, {value: "#ee3b70", id: 107, productId: 3}]
];
var arrayB = [
{
id: 1,
optionValue: [{value: "#002e63", id: 161, productId: 1}, {
value: "#0767b9",
id: 162,
productId: 1
}, {value: "#010b1d", id: 163, productId: 1}, {value: "#f4b7d4", id: 164, productId: 1}]
},
{
id: 2,
optionValue: [{value: "#EC7063", id: 93, productId: 2}, {
value: "#bf0000",
id: 94,
productId: 2
}, {value: "#44acd8", id: 102, productId: 2}, {value: "#ffdbdb", id: 103, productId: 2}]
},
{
id: 3,
optionValue: [{value: "#d861bd", id: 105, productId: 3}, {
value: "#609923",
id: 106,
productId: 3
}, {value: "#ee3b70", id: 107, productId: 3}]
},
{
id: 4,
optionValue: [{value: "#44acd8", id: 165, productId: 4}]
}
];
let result = [];
for (let i = 0; i < arrayB.length; i++) {
let selectedElem = [];
for (let j = 0; j < arrayB[i].optionValue.length; j++) {
arrayA.forEach(elemA => {
elemA.forEach(subElemA => {
if(subElemA.id === arrayB[i].optionValue[j].id) {
selectedElem.push(arrayB[i].optionValue[j]);
}
})
})
}
if (selectedElem.length !== 0){
arrayB[i].optionValue = selectedElem;
}
result.push(arrayB[i]);
}
console.log('result::', JSON.stringify(result, null, 2));

Edit multiple value in object based on array

Suppose I have:
const fixed = {pear: 100, apple: 4}
const arr = [
{name: 'P1', pear: 150, apple: 2},
{name: 'P2', pear: 50, apple: 5},
{name: 'P3', pear: 450, apple: 1},
{name: 'P4', pear: 100, apple: 3},
]
and I want to have:
const result = [
{name: 'P1', pear: -50, apple: 2},
{name: 'P2', pear: 50, apple: -1},
{name: 'P3', pear: -350, apple: 3},
{name: 'P4', pear: 0, apple: 1},
]
So result has the same items of arr but with edited apple and pear values based on fixed object values.
The new pear (and apple) value should be fixed.pear - oldPearValue, so for example, for arr[0]:
fixed.pear - arr[0].pear = 100 - 150 = -50 --> result[0].pear = -50
Here is what I tried:
function difference(fixed, value) {
return value - fixed
}
const fixed = {pear: 100, apple: 4}
const arr = [
{name: 'P1', pear: 150, apple: 2},
{name: 'P2', pear: 50, apple: 5},
{name: 'P3', pear: 450, apple: 1},
{name: 'P4', pear: 100, apple: 3},
]
const dataset = arr.flatMap((d) => {
Object.entries(fixed).forEach(([key, value]) => {
return { ...d, [key]: difference(value, d[key]) }
})
})
console.log(dataset)
as you can see, the result is [ undefined, undefined, undefined, undefined ]..
You could get the entries and map new properties.
const
fixed = { pear: 100, apple: 4 },
arr = [{ name: 'P1', pear: 150, apple: 2 }, { name: 'P2', pear: 50, apple: 5 }, { name: 'P3', pear: 450, apple: 1 }, { name: 'P4', pear: 100, apple: 3 }],
dataset = arr.map(d => ({ ...d, ...Object.fromEntries(Object
.entries(fixed)
.map(([k, v]) => [k, v - d[k]])
) }));
console.log(dataset);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If the fixed object has to be a fixed object with same fields, then you can try this
const fixed = { pear: 100, apple: 4 };
const arr = [
{ name: 'P1', pear: 150, apple: 2 },
{ name: 'P2', pear: 50, apple: 5 },
{ name: 'P3', pear: 450, apple: 1 },
{ name: 'P4', pear: 100, apple: 3 }
];
let data = arr.map((item) => {
return { ...item, pear: fixed.pear - item.pear, apple: fixed.apple - item.apple };
});
console.log('checkk', data);

JavaScript group similar objects by same value

I have an array of objects, I need to group them by the same value, I need to group them by a multidimensional array
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
[
Like this structure, all the same objects should be grouped by array inside an array.
[
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1}
],
[
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32}
],
[
{name: 'C', age: 15, id: 32},
]
]
I have tried like this but no luck.
const result = source.reduce((accumulator, item) => {
if (accumulator && accumulator.length) {
const found = accumulator.find(group => {
return group && group.length
? group.find(_transaction =>
// check the same object
return false
)
: false;
});
if (found) {
console.log(found);
}
} else {
accumulator.push([item]);
}
return accumulator;
}, []);
Create an object indexed by the name property, whose values are arrays containing the source items, then take the object's values:
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
const sourceItemsByName = {};
for (const obj of source) {
if (!sourceItemsByName[obj.name]) {
sourceItemsByName[obj.name] = [];
}
sourceItemsByName[obj.name].push(obj);
}
const output = Object.values(sourceItemsByName);
console.log(output);
You can reduce the array to a Map, using the name and age combination as the key. Then spread the Map.values() iterator back to an array:
const source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }];
const result = [... // spread the values iterator to an array
source.reduce((r, o) => {
const key = `${o.name}---${o.age}`; // generate a key with name and age
if(!r.has(key)) r.set(key, []); // add a new entry for key if it's not in the Map
r.get(key).push(o); // push the current object to the keys's entry
return r;
}, new Map())
.values() // get the Maps values iterator
];
console.log(result);
I think the following code is easier to understand. Hope, it will help you. Thanks.
UPDATED: As you need mentioned in a comment that you need name and age property as your key value.
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
var hashObject = {}
source.forEach(function(elem) {
var key = elem.name + elem.age;
if (hashObject[key]) {
hashObject[key].push(elem);
} else {
hashObject[key] = [elem];
}
});
var desiredArray = Object.values(hashObject);
console.log(desiredArray);
You could find the group array and if found add the object or add a new group to the result set.
const
source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }],
groups = ['name', 'age'],
grouped = source.reduce((r, o) => {
var temp = r.find(([q]) => groups.every(k => o[k] === q[k]));
if (temp) {
temp.push(o);
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An approach with a Map
const
source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }],
groups = ['name', 'age'],
grouped = Array.from(source
.reduce(
(m, o) =>
(k => m.set(k, [...(m.get(k) || []), o]))
(groups.map(k => o[k]).join('|')),
new Map)
.values()
);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Find Duplicates in array of transactions objects using JavaScript, and combined duplicates in array of array objects

I have a array of transactions objects, where i need to find duplicates based on property(A object is duplicate if it's all values are same except ID and TIME, time difference should be within 1 mins).
I need to combined identical duplicates transactions as a Array objects.
Below are the input of transactions.
I tried using Reduce functions but couldn't get the expected output.
var newArray = transactions.reduce(function(acc, curr) {
//finding Index in the array where the NamaCategory matched
var findIfduplicateExist = acc.findIndex(function(item) {
let accepoch = new Date(item.time).valueOf();
let currepoch= new Date(curr.time).valueof();
if(item.sourceAccount === curr.sourceAccount &&
item.targetAccount===curr.targetAccount &&
item.amount===curr.amount&&
accepoch<currepoch+(1*60*1000))
let obj={
'id':curr.id,
'sourceAccount':curr.sourceAccount,
'targetAccount':curr.targetAccount,
'time':curr.time,
'category':curr.category,
'amount':curr.amount
}
})
// if in the new array no such object exist, create a new object
if (findIfNameExist === -1) {
acc.push(obj)
} else {
// if attributes matches , then push the value
acc[findIfNameExist].value.push(curr)
}
return acc;
}, []);
Input transactions:
[
{
id: 3,
sourceAccount: 'A',
targetAccount: 'B',
amount: 100,
category: 'eating_out',
time: '2018-03-02T10:34:30.000Z'
},
{
id: 1,
sourceAccount: 'A',
targetAccount: 'B',
amount: 100,
category: 'eating_out',
time: '2018-03-02T10:33:00.000Z'
},
{
id: 6,
sourceAccount: 'A',
targetAccount: 'C',
amount: 250,
category: 'other',
time: '2018-03-02T10:33:05.000Z'
},
{
id: 4,
sourceAccount: 'A',
targetAccount: 'B',
amount: 100,
category: 'eating_out',
time: '2018-03-02T10:36:00.000Z'
},
{
id: 2,
sourceAccount: 'A',
targetAccount: 'B',
amount: 100,
category: 'eating_out',
time: '2018-03-02T10:33:50.000Z'
},
{
id: 5,
sourceAccount: 'A',
targetAccount: 'C',
amount: 250,
category: 'other',
time: '2018-03-02T10:33:00.000Z'
}
];
The Expected output is as below:
[
[
{
id: 1,
sourceAccount: "A",
targetAccount: "B",
amount: 100,
category: "eating_out",
time: "2018-03-02T10:33:00.000Z"
},
{
id: 2,
sourceAccount: "A",
targetAccount: "B",
amount: 100,
category: "eating_out",
time: "2018-03-02T10:33:50.000Z"
},
{
id: 3,
sourceAccount: "A",
targetAccount: "B",
amount: 100,
category: "eating_out",
time: "2018-03-02T10:34:30.000Z"
}
],
[
{
id: 5,
sourceAccount: "A",
targetAccount: "C",
amount: 250,
category: "other",
time: "2018-03-02T10:33:00.000Z"
},
{
id: 6,
sourceAccount: "A",
targetAccount: "C",
amount: 250,
category: "other",
time: "2018-03-02T10:33:05.000Z"
}
]
]
It will be easier (and more efficient) when you first get a copy of the transactions that is sorted by id. I assume that the id is an incremental number, so that later transactions always have a greater number. That way you only have to compare the timestamp with the last one in the accumulator:
// Example data
const transactions = [ { id: 3, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:34:30.000Z' }, { id: 1, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:00.000Z' }, { id: 6, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:05.000Z' }, { id: 4, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:36:00.000Z' }, { id: 2, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:50.000Z' }, { id: 5, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:00.000Z' } ];
const newArray = [...transactions].sort((a,b) => a.id - b.id).reduce( (acc, curr) => {
let group = acc[acc.length-1],
prev = group && group[group.length-1];
if (!prev || prev.sourceAccount !== curr.sourceAccount ||
prev.targetAccount !== curr.targetAccount ||
prev.amount !== curr.amount ||
Date.parse(prev.time) + (1*60*1000) < Date.parse(curr.time)) {
// different keys or larger time difference: create new group
acc.push(group = []);
}
group.push(curr);
return acc;
}, []);
console.log(newArray);
This can be done in concise way via one Array.sort, Array.reduce and Object.values:
const data = [{ id: 3, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:34:30.000Z' }, { id: 1, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:00.000Z' }, { id: 6, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:05.000Z' }, { id: 4, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:36:00.000Z' }, { id: 2, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:50.000Z' }, { id: 5, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:00.000Z' }]
const sort = arr => arr.sort((a,b) =>`${a.id}${a.time}`.localeCompare(`${b.id}${b.time}`))
const getTime = obj => new Date(obj.time).getTime()
const isDub = (arr, obj) => arr.length ? Math.abs(getTime(arr[arr.length-1]) - getTime(obj))/1000 > 60 : false
const result = Object.values(sort(data).reduce((r, c) => {
let key = [c.sourceAccount, c.targetAccount].join('-')
r[key] = isDub(r[key] || [], c) ? r[key] : [...r[key] || [], c]
return r
}, {}))
console.log(result)
You do need to pre-sort the array so that you only deal with the last entry when comparing for duplicate based on your within minute requirement.
You can do multi-column sort and then find duplicates in each group.
const SECONDS = 60;
const MILLISECONDS = 1000;
const getTimeDifference = (t1, t2) => {
return new Date(t1) - new Date(t2);
};
const multiLevelSort = (transactions = [], colsToSort = []) => {
return transactions.sort((a, b) => {
return colsToSort.reduce((acc, col) => {
if (acc !== 0 || a[col] == b[col]) {
return acc;
}
const c1 = a[col], c2 = b[col];
if (col === "time") {
return getTimeDifference(c1, c2) > 0 ? 1 : -1;
} else {
return c1 > c2 ? 1 : -1;
}
}, 0);
});
};
const isUniqueTransaction = (prev, curr, matchKeys = []) => {
if (!prev || !curr) {
return true;
}
return matchKeys.reduce((acc, key) => {
/* Current key is time then difference should be more than equal
* 1 min for transaction to be unique.
*/
if (key === "time") {
return (
acc ||
getTimeDifference(curr[key], prev[key]) >= 1 * SECONDS * MILLISECONDS
);
}
return acc || prev[key] !== curr[key];
}, false);
};
function findDuplicateTransactions(transactions = []) {
const matchingKeys = [
"sourceAccount",
"targetAccount",
"amount",
"category",
"time"
];
const sortedTransactions = multiLevelSort(transactions, matchingKeys);
let duplicates = [];
let group = [];
sortedTransactions.forEach((curr, idx, transactions) => {
// Previous Transaction find check if current trasaction is unique.
const prev = group && group[group.length - 1];
const isUnique = isUniqueTransaction(prev, curr, matchingKeys);
if (isUnique) {
if (group.length > 1) {
duplicates.push(group);
}
group = [];
}
group.push(curr);
});
// Push last group if it has more than 1 transaction
if (group.length > 1) {
duplicates.push(group);
}
// Sort duplicate trasaction groups based on first transaction in group
return duplicates.sort((a, b) => {
return getTimeDifference(a[0].time, b[0].time);
});
}
You can also use Array.sort and Array.forEach like below to achieve this
I have initially sorted array by concatenating property values (excluding id and time) and in increasing timestamp
let arr = [{ id: 3, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:34:30.000Z'},{ id: 1, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:00.000Z'},{ id: 6, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:05.000Z'},{ id: 4, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:36:00.000Z'},{ id: 2, sourceAccount: 'A', targetAccount: 'B', amount: 100, category: 'eating_out', time: '2018-03-02T10:33:50.000Z'},{ id: 5, sourceAccount: 'A', targetAccount: 'C', amount: 250, category: 'other', time: '2018-03-02T10:33:00.000Z'}];
let res = []
, getKey = ({id, time, ...rest}) => Object.entries(rest).map(([k, v]) => k + '-' + v).join(';')
, getTimeDiff = (t1, t2) => Math.abs(new Date(t1).getTime() - new Date(t2).getTime())
arr.sort((a,b) => {
let akey = getKey(a)
, bkey = getKey(b)
return akey.localeCompare(bkey) || +new Date(a.time) - +new Date(b.time)
})
.forEach((d, i, t) =>
i == 0 ||
(getKey(d) == getKey(t[i-1]) && getTimeDiff(t[i-1].time, d.time)/1000 < 60)
? res.push((res.pop() || []).concat(d))
: res.push([d])
)
console.log(res)

How to remove multiple items from an array with the same id, but keep one while increasing its "qty"?

I have an Array like so:
const arr = [
{ price: 12, desc: 'desc', id: 123, qty: 1 },
{ price: 12, desc: 'desc', id: 123, qty: 1 },
{ price: 12, desc: 'desc', id: 123, qty: 1 }
];
I want to be able to delete x amount of items until there is only one item left with the id of 123. However I also want to increase that item's qty by however many other items there were with that same id.
So for example I want a resulting array like so:
const result = [ { price: 12, desc: 'desc', id: 123, qty: 3 } ];
You can use the function reduce to group and count the matched ids.
const arr = [{ price: 12, desc: 'desc', id: 123, qty: 1 },{ price: 12, desc: 'desc', id: 123, qty: 1 },{ price: 12, desc: 'desc', id: 123, qty: 1 }];
// The function Object.values returns the values of every object, for example:
// accumulator = {
// "123": { price: 12, desc: 'desc', id: 123, qty: 3 }
// }
// The function Object.values returns:
// { price: 12, desc: 'desc', id: 123, qty: 3 }
const result = Object.values(arr.reduce((a, c) => {
// The accumulator 'a' will contain objects as follow:
// {'123': {id: 123, desc: 'desc'...., qty: 2}}
// This line checks for the current object with 'c.id'
// If that object doesn't exist, a new object is created with
// a further property called 'qty' and then we add +1
(a[c.id] || (a[c.id] = Object.assign(c, {qty: 0}))).qty++;
return a;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources