{
"ethereum": {
"balance":"2",
"value":1382.4,
"id":"ethereum",
"name":"Ethereum",
"symbol":"ETH",
"rank":"2",
"price_usd":"691.204",
"24h_volume_usd":"2420600000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.51",
"percent_change_7d":"0.98",
"percentage":14.34
},
"bitcoin": {
"balance":"1",
"value":8255.95,
"id":"bitcoin",
"name":"Bitcoin",
"symbol":"BTC",
"rank":"1",
"price_usd":"8255.96",
"24h_volume_usd":"6128880000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.43",
"percent_change_7d":"-3.49",
"percentage":85.66
}
}
The above object was converted from this Array below then saved into localStorage.
What I'm trying to do is re-create the following Array:
[
{
24h_volume_usd: "6124340000.0",
balance: "1",
id: "bitcoin",
name: "Bitcoin",
percent_change_1h: "-0.1",
percent_change_7d: "-3.46",
percent_change_24h: "0.47",
percentage: 85.66,
price_usd: "8256.98",
rank: "1",
symbol: "BTC",
value: 8256.98
},
{
4h_volume_usd: "2420170000.0",
balance: "2",
id: "ethereum",
name: "Ethereum",
percent_change_1h: "-0.07",
percent_change_7d: "0.95",
percent_change_24h: "0.49",
percentage: 14.34,
price_usd: "691.074",
rank: "2",
symbol: "ETH",
value: 1382.14
}
]
The Array to Object logic
export const calculatePercentage = (portfolio, coin) => {
portfolio.push(coin);
const addValue = c => c.value;
const values = R.chain(addValue, portfolio);
const total = values.reduce((acc, val) => acc + val);
const updatedPortfolio = portfolio.map((c) => {
c.percentage = round((c.value / total) * 100);
return c;
});
const moonPortfolio = arrayToObject(updatedPortfolio);
// Local Storage saved here:
window.localStorage.setItem('moonPortfolio', JSON.stringify(moonPortfolio));
return updatedPortfolio;
};
You can use the Object.values method to get all values in the object in an array:
const object = {
"ethereum": {
"balance":"2",
"value":1382.4,
"id":"ethereum",
"name":"Ethereum",
"symbol":"ETH",
"rank":"2",
"price_usd":"691.204",
"24h_volume_usd":"2420600000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.51",
"percent_change_7d":"0.98",
"percentage":14.34
},
"bitcoin": {
"balance":"1",
"value":8255.95,
"id":"bitcoin",
"name":"Bitcoin",
"symbol":"BTC",
"rank":"1",
"price_usd":"8255.96",
"24h_volume_usd":"6128880000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.43",
"percent_change_7d":"-3.49",
"percentage":85.66
}
}
const array = Object.values(object)
console.log(array)
Object.values is what you are looking for.
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in
var o = {
"ethereum": {
"balance":"2",
"value":1382.4,
"id":"ethereum",
"name":"Ethereum",
"symbol":"ETH",
"rank":"2",
"price_usd":"691.204",
"24h_volume_usd":"2420600000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.51",
"percent_change_7d":"0.98",
"percentage":14.34
},
"bitcoin": {
"balance":"1",
"value":8255.95,
"id":"bitcoin",
"name":"Bitcoin",
"symbol":"BTC",
"rank":"1",
"price_usd":"8255.96",
"24h_volume_usd":"6128880000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.43",
"percent_change_7d":"-3.49",
"percentage":85.66
}
};
var x = Object.values(o);
console.log(x)
The below code can also be used to get the array containing the objects.
// Object to string conversion (just to test)
var stringifiedObj = JSON.stringify({
"ethereum":{
"balance":"2",
"value":1382.4,
"id":"ethereum",
"name":"Ethereum",
"symbol":"ETH",
"rank":"2",
"price_usd":"691.204",
"24h_volume_usd":"2420600000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.51",
"percent_change_7d":"0.98",
"percentage":14.34
},
"bitcoin": {
"balance":"1",
"value":8255.95,
"id":"bitcoin",
"name":"Bitcoin",
"symbol":"BTC",
"rank":"1",
"price_usd":"8255.96",
"24h_volume_usd":"6128880000.0",
"percent_change_1h":"0.02",
"percent_change_24h":"0.43",
"percent_change_7d":"-3.49",
"percentage":85.66
}
});
var obj = JSON.parse(stringifiedObj); // string formed object to real object conversion
var arr = []; // Empty array
for(var key in obj){
arr.push(obj[key]); // Push item(object) into array
}
console.log(arr);
/*
[ { balance: '2',
value: 1382.4,
id: 'ethereum',
name: 'Ethereum',
symbol: 'ETH',
rank: '2',
price_usd: '691.204',
'24h_volume_usd': '2420600000.0',
percent_change_1h: '0.02',
percent_change_24h: '0.51',
percent_change_7d: '0.98',
percentage: 14.34 },
{ balance: '1',
value: 8255.95,
id: 'bitcoin',
name: 'Bitcoin',
symbol: 'BTC',
rank: '1',
price_usd: '8255.96',
'24h_volume_usd': '6128880000.0',
percent_change_1h: '0.02',
percent_change_24h: '0.43',
percent_change_7d: '-3.49',
percentage: 85.66 } ]
*/
Thanks.
It can also be done during parsing:
var j = '{"ethereum":{"balance":"2","value":1382.4,"id":"ethereum","name":"Ethereum","symbol":"ETH","rank":"2","price_usd":"691.204","24h_volume_usd":"2420600000.0","percent_change_1h":"0.02","percent_change_24h":"0.51","percent_change_7d":"0.98","percentage":14.34},"bitcoin":{"balance":"1","value":8255.95,"id":"bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"8255.96","24h_volume_usd":"6128880000.0","percent_change_1h":"0.02","percent_change_24h":"0.43","percent_change_7d":"-3.49","percentage":85.66}}';
var a = [];
JSON.parse(j, function(k, v) {
if (v.id) a.push(v);
else return v;
})
console.log( a );
Related
I have a response value which is dynamic which i need to store in redux state,
Response consist of array of object and and name
ex :
{data:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
{name:"def",age:"15",id:"20"}
]
name: "abc"
}
So if the name is same I need to create array with the name.
Expected :
abc:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
]
something I tried
data.map(function(o) {
if(data.name ==o.name)
return name[o];
});
If you're wanting a new object with a key of the name property you could try something like this
const response = {
data: [{
name: "abc",
age: "10",
id: "10"
},
{
name: "abc",
age: "15",
id: "20"
},
{
name: "def",
age: "15",
id: "20"
},
],
name: "abc"
}
const createSet = (someData) => {
let key = someData.name
let data = someData.data.filter(e => e.name === key)
return {
[key]: data
}
}
console.log(createSet(response))
You can extract duplicated using reduce and filter :
var data = {
data:[
{name:"abc",age:"10",id:"10"},
{name:"abc",age:"15",id:"20"},
{name:"def",age:"15",id:"20"}
],
name: "abc"
}
const lookup = data.data.reduce((a, e) => {
a[e.name] = ++a[e.name] || 0;
return a;
}, {});
console.log(data.data.filter(e => lookup[e.name]));
Here is the scenario I am looking at:
I want to reduce these objects
const data = [
{
id: 1,
totalAmount: 1500,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 12,
isRefund: false,
},
{
id: 2,
totalAmount: 200,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 2,
isRefund: true,
},
{
id: 3,
totalAmount: 200,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 1,
isRefund: true,
},
];
and I found a solution that adds their values:
const deepMergeSum = (obj1, obj2) => {
return Object.keys(obj1).reduce((acc, key) => {
if (typeof obj2[key] === 'object') {
acc[key] = deepMergeSum(obj1[key], obj2[key]);
} else if (obj2.hasOwnProperty(key) && !isNaN(parseFloat(obj2[key]))) {
acc[key] = obj1[key] + obj2[key]
}
return acc;
}, {});
};
const result = data.reduce((acc, obj) => (acc = deepMergeSum(acc, obj)));
const array = []
const newArray = [...array, result]
which results to:
const newArray = [
{
id: 6,
totalAmount: 1900,
date: '2021-01-012021-01-012021-01-01',
vendor_id: 6,
totalTransaction: 15
}
]
And now my problem is I don't know yet how to work this around to have my expected output which if isRefund is true, it must be subtracted instead of being added, retain the vendor_id and also the concatenated date instead of only one entry date:
const newArray = [
{
id: 1(generate new id if possible),
totalAmount: 1100,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 15,
isRefund: null(this can be removed if not applicable),
},
];
I will accept and try to understand any better way or workaround for this. Thank you very much.
As you want custom behaviour for several fields, and don't need the recursive aspect of the merge, I would suggest you create a custom merge function, specific to your business logic:
const data = [{id: 1,totalAmount: 1500,date: '2021-01-01',vendor_id: 2,totalTransaction: 12,isRefund: false,},{id: 2,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 2,isRefund: true,},{id: 3,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 1,isRefund: true,},];
function customMerge(a, b) {
if (a.vendor_id !== b.vendor_id || a.date !== b.date) {
throw "Both date and vendor_id must be the same";
}
return {
id: Math.max(a.id, b.id),
totalAmount: (a.isRefund ? -a.totalAmount : a.totalAmount)
+ (b.isRefund ? -b.totalAmount : b.totalAmount),
date: a.date,
vendor_id: a.vendor_id,
totalTransaction: a.totalTransaction + b.totalTransaction
};
}
const result = data.reduce(customMerge);
if (data.length > 1) result.id++; // Make id unique
console.log(result);
You could also reintroduce the isRefund property in the result for when the total amount turns out to be negative (only do this when data.length > 1 as otherwise result is just the original single object in data):
result.isRefund = result.totalAmount < 0;
result.totalAmount = Math.abs(result.totalAmount);
Distinct results for different dates and/or vendors
Then use a "dictionary" (plain object or Map) keyed by date/vendor combinations, each having an aggregating object as value.
To demonstrate, I added one more object in the data that has a different date and amount of 300:
const data = [{id: 1,totalAmount: 1500,date: '2021-01-01',vendor_id: 2,totalTransaction: 12,isRefund: false,},{id: 2,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 2,isRefund: true,},{id: 3,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 1,isRefund: true,},{id: 4,totalAmount: 300,date: '2021-01-02',vendor_id: 2,totalTransaction: 1,isRefund: false,}];
function customMerge(acc, {id, date, vendor_id, totalAmount, totalTransaction, isRefund}) {
let key = date + "_" + vendor_id;
if (!(id <= acc.id)) acc.id = id;
acc[key] ??= {
date,
vendor_id,
totalAmount: 0,
totalTransaction: 0
};
acc[key].totalAmount += isRefund ? -totalAmount : totalAmount;
acc[key].totalTransaction += totalTransaction;
return acc;
}
let {id, ...grouped} = data.reduce(customMerge, {});
let result = Object.values(grouped).map(item => Object.assign(item, { id: ++id }));
console.log(result);
it could be help, if you are looking for the same output, can add other checklist based on your requirement, for filtered date, logic would be little different but the output will be same.
const getTotalTranscation = () =>
transctionLists.reduce((prev, current) => {
const totalAmount = current.isRefund
? prev.totalAmount - current.totalAmount
: prev.totalAmount + current.totalAmount;
const totalTransaction = current.isRefund
? prev.totalTransaction - current.totalTransaction
: prev.totalTransaction + current.totalTransaction;
return {
...current,
id: current.id + 1,
totalAmount,
totalTransaction,
isRefund: totalAmount < 0,
};
});
I am working on an application where I need to get combine the object of same department based on the
conditions provided in the second Array and attach the relation to the object.
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"}
]
Output
outArr ={
[{"ID":"1","NAME":"KEN","DEPT1":"CSE","REL":"AND"},
{"ID":"2","NAME":"MARK","DEPT2":"IT","REL":"AND"}], //Arr1
[{"ID":"3","NAME":"TOM","DEPT3":"ECE","REL":"OR"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB","REL":"OR"}], //Arr2
[{"ID":"5","NAME":"TIM","DEPT5":"SEC"}] //Arr3
}
Code:
let condArr=[],outArr,i=1;
inArr1.forEach(condt => {
let dept = Object.keys(condt)[0];
let tmparr = dept.split("D");
tmparr.shift()
condArr.push(tmparr)
});
inArr2.forEach(condt => {
if(condArr.includes(inArr2.D+i)){
i++;
outArr.push(inArr2);
}
});
Your code has a bit confused logic, i would suggest rather this
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"},{"D5D6":"AND"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"},
{"ID":"6","NAME":"TLA","DEPT6":"SEC"},
]
// first lets create object of ids as keys and conditions as values
const [keys, conditions] = inArr1.reduce((agg, cond, index) => {
Object.entries(cond).forEach(([key, value]) => {
key.split('D').forEach(v => { if (v) agg[0][v] = { value, index }})
agg[1].push([])
})
return agg
}, [{}, []]) // {1: "AND", 2: "AND", 3: "OR", 4: "OR"}
conditions.push([])
// and now just map over all elements and add condition if we found id from the keys
inArr2.forEach(item => {
const cond = keys[item.ID]
if (cond) conditions[cond.index].push({...item, REL: cond.value})
else conditions[conditions.length - 1].push(item)
})
const res = conditions.filter(v => v.length)
console.log(res)
You could store the goups by using the ID and use new objects.
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }],
inArr2 = [{ ID: "1", NAME: "KEN", DEPT1: "CSE" }, { ID: "2", NAME: "MARK", DEPT2: "IT" }, { ID: "3", NAME: "TOM", DEPT3: "ECE" }, { ID: "4", NAME: "SHIV", DEPT4: "LIB" }, { ID: "5", NAME: "TIM", DEPT5: "SEC" }],
groups = inArr1.reduce((r, o) => {
Object.entries(o).forEach(([k, REL]) => {
var object = { REL, group: [] };
k.match(/[^D]+/g).forEach(id => r[id] = object);
});
return r;
}, {}),
grouped = inArr2.reduce((r, o) => {
var { REL, group } = groups[o.ID] || {};
if (group) {
if (!group.length) r.push(group);
group.push(Object.assign({}, o, { REL }));
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
can try other solution:
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }, { D6D7: "XOR" }];
let inArr2 = [
{ ID: "1", NAME: "KEN", DEPT1: "CSE" },
{ ID: "2", NAME: "MARK", DEPT2: "IT" },
{ ID: "3", NAME: "TOM", DEPT3: "ECE" },
{ ID: "4", NAME: "SHIV", DEPT4: "LIB" },
{ ID: "5", NAME: "TIM", DEPT5: "SEC" },
{ ID: "9", NAME: "BAR", DEPT5: "XYZ" },
{ ID: "6", NAME: "FOO", DEPT5: "XYZ" },
];
let unmatchedArr = []
let matchedArr = inArr2.reduce((acc, obj) => {
// getting index matched from inArr1 objects key
const indexMatched = getIndexMatch(obj.ID);
// creating index if not exists
if (!acc[indexMatched] && indexMatched !== null) acc[indexMatched] = [];
// if some index matched it merge current obj with DEL property with inArr1[indexMatched] key => value
return indexMatched !== null
? acc[indexMatched].push({
...obj,
DEL: inArr1[indexMatched][Object.keys(inArr1[indexMatched])[0]]
})
// pushing on unmatchedArr
: unmatchedArr.push(obj)
, acc
}, []);
function getIndexMatch(id) {
for (const [index, obj] of inArr1.entries()) {
for (const key of Object.keys(obj)) {
// spliting only digits of the current key of object
if (key.match(/\d/g).includes(id)) return index; // returning index of inArr1 if is included
}
}
return null;
}
// merging arrays
const result = [...matchedArr, unmatchedArr];
console.log(result);
I am trying to make a basic stock keeping tool in which a user can input a bar code and receive information about the product attached to that bar code such as the product name and quantity available etc.
To organise my data I have created a array called products and made an object for each product.
My array currently looks like this :
var products = [
{
brand:"Healthy Boy",
product:"Sweet Chilli Sauce",
size: 300,
measurement: "ml",
barcode:"00909274636143",
quantity:"2"
},
{
brand:"Golden Dragon",
product:"rice",
size: 1,
measurement: "kg",
barcode:"5623593845",
quantity:"5"
},
{
brand:"Golden Dragon",
product:"rice",
size: 1,
measurement: "kg",
barcode:"5623593845",
quantity:"5"
}
];
I use two functions to search via the inputting of a bar code to see if the bar code is part of the array. The functions look like this:
function isBarcodeValid (barcode){
for(var i = 0; i < products.length; i++) {
if(products [i].barcode === barcode){
return true;
}
}
return false;
}
function displayBarcode (barcode){
if (isBarcodeValid(barcode)){
console.log("barcode exists");
} else {
console.log("invalid barcode");
}
}
At the moment I am logging to the console whether or not the bar code is present in the array but is there a way that I can display the information related to each bar code contained within the object?
Thanks
instead of return boolean, return the object or null instead:
function isBarcodeValid (barcode){
for(var i = 0; i < products.length; i++) {
if(products [i].barcode === barcode){
return products[i];
}
}
return null;
}
function displayBarcode (barcode){
var v = isBarcodeValid(barcode);
if (v){
console.log(v);
} else {
console.log("invalid barcode");
}
}
var products = [
{
brand:"Healthy Boy",
product:"Sweet Chilli Sauce",
size: 300,
measurement: "ml",
barcode:"00909274636143",
quantity:"2"
},
{
brand:"Golden Dragon",
product:"rice",
size: 1,
measurement: "kg",
barcode:"5623593845",
quantity:"5"
},
{
brand:"Golden Dragon",
product:"rice",
size: 1,
measurement: "kg",
barcode:"5623593845",
quantity:"5"
}
];
function findBarcode (barcode){
for(var i = 0; i < products.length; i++) {
const p = products[i]
if(p.barcode === barcode){
return p;
}
}
}
function displayBarcode (barcode){
const barCode = findBarcode(barcode)
const barcodeIsValid = !!barCode
if (barcodeIsValid){
console.log(barCode, "barcode exists");
} else {
console.log("invalid barcode");
}
}
displayBarcode('00909274636143') //present
displayBarcode('not-a-barcode') //present
you can return the identified product when you're filtering to see if it is present
You can return the product taking advantage of Array methods and ES6 syntax in javascript to perform a find instead a workaround with for and breaking the for loop retuning data in the middle:
'use strict';
const products = [{
brand: 'Healthy Boy',
product: 'Sweet Chilli Sauce',
size: 300,
measurement: 'ml',
barcode: '00909274636143',
quantity: '2',
}, {
brand: 'Golden Dragon',
product: 'rice',
size: 1,
measurement: 'kg',
barcode: '5623593845',
quantity: '5',
}, {
brand: 'Golden Dragon',
product: 'rice',
size: 1,
measurement: 'kg',
barcode: '5623593845',
quantity: '5',
}];
/**
* Look for a given barcode and return the product or null if barcode doesn't exist
* #param {String} barcode
* #return {Object} product data or null
* */
function isBarcodeValid (barcode){
return products.find(p => p.barcode === barcode);
}
function displayBarcode (barcode) {
const product = isBarcodeValid(barcode);
if (product){
console.log(`barcode exists: ${product}`);
} else {
console.log('invalid barcode');
}
}
Saw it got answered, but just for some spice:
const isValid=(barcode)=>{
let a=products.findIndex(x=>x.barcode===barcode)
a>-1?(console.log("barcode exists: "),
Object.keys(products[a]).forEach(y=>console.log(y, ":",products[a][y]))):
console.log("invalid barcode");
}
isValid("5623593845");
You can simply return the valid Object, rather than log the properties as I did here.
This ends up being quite concise if you are to utilize ES6 (arrow functions/destructuring) and Array.prototype.find():
var products = [{ brand:"Healthy Boy", product:"Sweet Chilli Sauce", size: 300, measurement: "ml", barcode:"00909274636143", quantity:"2" }, { brand:"Golden Dragon", product:"rice", size: 1, measurement: "kg", barcode:"5623593845", quantity:"5" }, { brand:"Golden Dragon", product:"rice", size: 1, measurement: "kg", barcode:"5623593845", quantity:"5" } ];
const getBarcode = (bc) => products.find(({barcode}) => barcode == bc)
const displayBarcode = (bc) => console.log(getBarcode(bc) || 'invalid barcode')
displayBarcode(00909274636143)
displayBarcode(009092746111136143)
The idea is if you find it you would simply output the object. If not the || (or) operator would evaluate the invalid barcode string.
This question already has answers here:
JavaScript merging objects by id [duplicate]
(18 answers)
Closed 3 years ago.
I have an array:
[
{
assignmentId:17,
email:"john.smith#email.com"
expectation: "Make sure to proofread!",
firstName:"John"
id:23
ignoreForFeedback: true
lastName:"Smith"
level:2
levelFraction:null
score:35
},
{
assignmentId:17
countsPerCategory: Array(4)
email:"john.smith#email.com"
firstName:"John"
frequentErrors: Array(5)
id:23
ignoreForGrading: true
lastName:"Smith"
},
{
assignmentId:17,
email:"cl#email.com"
expectation: "cite sources",
firstName:"Cindy"
id:45
ignoreForFeedback: true
lastName:"Lee"
level:2
levelFraction:null
score:32
},
{
assignmentId:17
countsPerCategory: Array(4)
email:"cl#email.com"
firstName:"Cindy"
frequentErrors: Array(5)
id:45
ignoreForGrading: true
lastName:"Lee"
}
]
I want to combine the Objects with the same 'id' into the same object within the array. Their common keys should also be combined (eg: 'firstName', 'email'). Can someone suggest the best way to do this? Either with ES6 or Lodash
You can use lodash#groupBy to group all items in the array by id and then use lodash#map with an iteratee of lodash#assign that is wrapped with a lodash#spread to make the array callback as a list of arguments for lodash#assgin.
var result = _(array)
.groupBy('id')
.map(_.spread(_.assign))
.value();
var array = [
{
assignmentId:17,
email:"john.smith#email.com",
expectation: "Make sure to proofread!",
firstName:"John",
id:23,
ignoreForFeedback: true,
lastName:"Smith",
level:2,
levelFraction:null,
score:35
},
{
assignmentId:17,
countsPerCategory: Array(4),
email:"john.smith#email.com",
firstName:"John",
frequentErrors: Array(5),
id:23,
ignoreForGrading: true,
lastName:"Smith"
},
{
assignmentId:17,
email:"cl#email.com",
expectation: "cite sources",
firstName:"Cindy",
id:45,
ignoreForFeedback: true,
lastName:"Lee",
level:2,
levelFraction:null,
score:32
},
{
assignmentId:17,
countsPerCategory: Array(4),
email:"cl#email.com",
firstName:"Cindy",
frequentErrors: Array(5),
id:45,
ignoreForGrading: true,
lastName:"Lee"
}
];
var result = _(array)
.groupBy('id')
.map(_.spread(_.assign))
.value();
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Here's an alternative solution that uses Array#filter which takes advantage of the 2nd argument of the Array#filter which gives context to the filter's callback function. We use the this context as a mechanism to store cached objects by their id and then use this to decide whether to retain these objects from the array or not.
var result = array.filter(function(v) {
return this[v.id]?
!Object.assign(this[v.id], v):
(this[v.id] = v);
}, {});
var array = [
{
assignmentId:17,
email:"john.smith#email.com",
expectation: "Make sure to proofread!",
firstName:"John",
id:23,
ignoreForFeedback: true,
lastName:"Smith",
level:2,
levelFraction:null,
score:35
},
{
assignmentId:17,
countsPerCategory: Array(4),
email:"john.smith#email.com",
firstName:"John",
frequentErrors: Array(5),
id:23,
ignoreForGrading: true,
lastName:"Smith"
},
{
assignmentId:17,
email:"cl#email.com",
expectation: "cite sources",
firstName:"Cindy",
id:45,
ignoreForFeedback: true,
lastName:"Lee",
level:2,
levelFraction:null,
score:32
},
{
assignmentId:17,
countsPerCategory: Array(4),
email:"cl#email.com",
firstName:"Cindy",
frequentErrors: Array(5),
id:45,
ignoreForGrading: true,
lastName:"Lee"
}
];
var result = array.filter(function(v) {
// does this `id` exist?
return this[v.id]?
// assign existing object with the same id
// from the `this` cache object. Make sure
// to negate the resulting object with a `!`
// to remove this value from the array
!Object.assign(this[v.id], v):
// Assign the value from the `this` cache.
// This also retains this value from the existing
// array
(this[v.id] = v);
}, {});
console.log(result);
body > div { min-height: 100%; top: 0; }
You can use JavaScript's built in Array.reduce() method. The idea is you can create a map with the IDs and use lodash.merge() method (or whatever method you choose for merging objects) to merge all of the objects with the same ID into a single object. Then you can use .map() on the idMap you created to get the objects back into a single array.
var data = [{
assignmentId: 17,
email: "john.smith#email.com",
expectation: "Make sure to proofread!",
firstName: "John",
id: 23,
ignoreForFeedback: true,
lastName: "Smith",
level: 2,
levelFraction: null,
score: 35
},
{
assignmentId: 17,
countsPerCategory: Array(4),
email: "john.smith#email.com",
firstName: "John",
frequentErrors: Array(5),
id: 23,
ignoreForGrading: true,
lastName: "Smith"
},
{
assignmentId: 17,
email: "cl#email.com",
expectation: "cite sources",
firstName: "Cindy",
id: 45,
ignoreForFeedback: true,
lastName: "Lee",
level: 2,
levelFraction: null,
score: 32
},
{
assignmentId: 17,
countsPerCategory: Array(4),
email: "cl#email.com",
firstName: "Cindy",
frequentErrors: Array(5),
id: 45,
ignoreForGrading: true,
lastName: "Lee"
}
];
var idMap = data.reduce(function(result, current) {
if (result[current.id] == null) {
result[current.id] = current;
} else {
_.merge(result[current.id], current);
}
return result;
}, {});
var results = Object.keys(idMap).map(function(key) {
return idMap[key];
});
console.log(results);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
What I can suggest is to use a combination of forEach() and some() methods to iterate the array elements and test if the iterated object id is already processed or not.
This is the solution:
var merged = [];
arr.forEach(function(item) {
var idx;
var found = merged.some(function(el, i) {
idx = el.id === item.id ? i : null;
return el.id === item.id;
});
if (!found) {
merged.push(item);
} else if (idx !== null) {
for (k in Object.keys(item)) {
if (item.hasOwnProperty(k)) {
merged[idx][k] = item[k];
}
}
}
});
Working Demo:
var arr = [{
assignmentId: 17,
email: "john.smith#email.com",
expectation: "Make sure to proofread!",
firstName: "John",
id: 23,
ignoreForFeedback: true,
lastName: "Smith",
level: 2,
levelFraction: null,
score: 35
},
{
assignmentId: 17,
countsPerCategory: [],
email: "john.smith#email.com",
firstName: "John",
frequentErrors: [],
id: 23,
ignoreForGrading: true,
lastName: "Smith"
},
{
assignmentId: 17,
email: "cl#email.com",
expectation: "cite sources",
firstName: "Cindy",
id: 45,
ignoreForFeedback: true,
lastName: "Lee",
level: 2,
levelFraction: null,
score: 32
},
{
assignmentId: 17,
countsPerCategory: [],
email: "cl#email.com",
firstName: "Cindy",
frequentErrors: [],
id: 45,
ignoreForGrading: true,
lastName: "Lee"
}
];
var merged = [];
arr.forEach(function(item) {
var idx;
var found = merged.some(function(el, i) {
idx = el.id === item.id ? i : null;
return el.id === item.id;
});
if (!found) {
merged.push(item);
} else if (idx !== null) {
for (k in Object.keys(item)) {
if (item.hasOwnProperty(k)) {
merged[idx][k] = item[k];
}
}
}
});
console.log(merged);
Thank you for your help everyone, but I ended up going with my own implementation.
let ids = [];
let combinedUsers = [];
users.forEach(function (user) {
ids.push(user.id);
});
ids = _.uniq(ids);
ids.forEach(function(id){
let user = users.filter(function(userObj){
return id === userObj.id
});
if(user.length > 1){
user = Object.assign(user[0], user[1]);
combinedUsers.push(user);
} else {
combinedUsers.push(user[0]);
}
});
return combinedStudents;