ImmutableJS: merge two list of objects, without duplicating them - javascript

Supposing I have the below:
var allFoods = Immutable.List();
var frenchFood = Immutable.List([
{
'type': 'french fries',
'price': 3
},
{
'type': 'petit gateau',
'price': 40
},
{
'type': 'croissant',
'price': 20
},
]);
var fastFood = Immutable.List([
{
'type': 'cheeseburger',
'price': 5
},
{
'type': 'vegan burger',
'price': 20
},
{
'type': 'french fries',
'price': 3
}
]);
I want to merge both lists, in a way that I also remove dupes (in this case, french fries), so the expected result would be:
{
'type': 'french fries', // keep the first french fries
'price': 3
},
{
'type': 'petit gateau',
'price': 40
},
{
'type': 'croissant',
'price': 20
},
{
'type': 'cheeseburger',
'price': 5
},
{
'type': 'vegan burger',
'price': 20
}
What I'm trying (doesn't remove dupes):
allFoods = frenchFood.concat(fastFood);
allFoods = allFoods.filter(function(item, pos) {
return allFoods.indexOf(item) === pos;
});
Which returns arrays merged, but still duplicated.
What am I missing?

const allFoods = frenchFood.concat(fastFood.filter((item) =>
frenchFood.indexOf(item) < 0
));

I would use reduce
var result = frenchFood.concat(fastFood).reduce( (reduction, food) => {
if(reduction[food.type]) {
return reduction;
} else {
return reduction.set([food.type], food);
}
}, new Immutable.Map()).valueSeq().toList();

I would highly encourage you to not nest js objects inside immutable structures. Better to wrap those objects in an Immutable.Map() or do Immutable.fromJS(yourJsObj).
Least amount of code
const results = Immutable.Set(frenchFood).union(Immutable.Set(fastFood));
However #rooftop answer fastest
https://jsperf.com/union-vs-concat-immutable

I found a best solution (for me) on medium, link to origin answer is dead: https://medium.com/#justintulk/merging-and-deduplicating-data-arrays-with-array-reduce-efaa4d7ef7b0
const arr1 = [
{ id: 1, name: 'Array 1-1' },
{ id: 2, name: 'Array 1-2' },
{ id: 3, name: 'Array 1-3' }
]
const arr2 = [
{ id: 1, name: 'Array 2-1' },
{ id: 3, name: 'Array 2-3' },
{ id: 4, name: 'Array 2-4' }
]
const mergeArrObjectsUnique = (currentArr, newArr) => {
let obj = {}
currentArr.forEach(item => {
obj[item.id] = item
})
newArr.forEach(item => {
obj[item.id] = item
})
let result = [];
for(let p in obj) {
if(obj.hasOwnProperty(p))
result.push(obj[p])
}
console.log('result: ', result)
return result
}
mergeArrObjectsUnique(arr1, arr2)

Related

How can i merge objects on the basis of same value in array of objects

I have array of multiple objects. Every object have a key "id". I want to merge objects On the basis of same value of "id".
var obj = [{'id': 1, 'title': 'Hi'}, {'id': 1, 'description': 'buddy'}, {'id': 2, 'title': 'come'}, {'id': 2, 'description': 'On'}]
And i want output something like that
var new_obj = [{'id': 1, 'title': 'Hi' 'description': 'buddy'}, {id: 2, 'title': 'come', 'description': 'on'}]
This way works :
let toMerge = [{'id': 1, 'title': 'Hi'}, {'id': 1, 'description': 'buddy'}, {'id': 2, 'title': 'come'}, {'id': 2, 'description': 'On'}]
let merged = []
for ( let object of toMerge ) {
// if object not already merged
if(!merged.find(o => o.id === object.id)) {
// filter the same id's objects, merge them, then push the merged object in merged array
merged.push(toMerge.filter(o => o.id === object.id).reduce((acc, val) => {return {...acc, ...val}}))
}
}
You could reduce the array by finding an object with the same id and assign the object to found or take a new object.
var array = [{ id: 1, title: 'Hi' }, { id: 1, description: 'buddy' }, { id: 2, title: 'come' }, { id: 2, description: 'On' }],
result = array.reduce((r, o) => {
var temp = r.find(({ id }) => o.id === id);
if (!temp) r.push(temp = {});
Object.assign(temp, o);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a solution
const data = [
{
id: 1,
title: "Hi"
},
{
description: "buddy",
id: 1
},
{
id: 2,
title: "come"
},
{
description: "On",
id: 2
}
]
const f = (data) =>
Object.values(data.reduce(
(y, x) => ({
...y,
[x.id]: {
...x,
...(y[x.id] || {})
},
}),
{},
))
console.log(f(data))
Here's a solution using Ramda.js
const data = [
{
id: 1,
title: "Hi"
},
{
description: "buddy",
id: 1
},
{
id: 2,
title: "come"
},
{
description: "On",
id: 2
}
]
const f = R.compose(R.values, R.reduceBy(R.merge, {}, R.prop('id')))
console.log(f(data))
<script src="//cdn.jsdelivr.net/npm/ramda#latest/dist/ramda.min.js"></script>

how to pivot using javascritpt?

I'm facing an array of objects where it is possible to have more than one object with the same value of id. When the value of id is the same, the value of price will also be the same.
Here's an example:
[{
'id': 'A', 'price': 1, 'quantity': 1
}, {
'id': 'A', 'price': 1, 'quantity': 2
}, {
'id': 'B', 'price': 1, 'quantity': 1
}]
The result should be an array where only one object appears for every id. Moreover, quantity must be populated with the sum of the quantities in the repeated values of id.
Here's the expected result for the example above:
[{
'id': 'A','price': 1, 'quantity': 3
}, {
'id': 'B','price': 1, 'quantity': 1
}]
Actually, I need to pivot.
I would like to avoid jQuery and external calls. Is it possible to accomplish this using only JS functions?
Arrays of objects are a little messy to work with, but this is do-able.
const list = [
{'id': 'A', 'price': 1, 'quantity': 1},
{'id': 'A', 'price': 1, 'quantity': 2},
{'id': 'B', 'price': 1, 'quantity': 1}
];
// Objects are easier to work with. An intermediate step to combine entries by Id.
const intermediate = list.reduce((a, { id, price, quantity }) => {
a[id] = a[id] || { id, price, quantity: 0}; // Copy id & price if the entry doesn't exist yet.
a[id].quantity += quantity; // Add quantity.
return a;
}, {});
// Map it all back to an array of objects.
const result = Object.keys(intermediate).map(id => intermediate[id]);
console.log(result);
Here's a ES5 version:
var list = [
{'id': 'A', 'price': 1, 'quantity': 1},
{'id': 'A', 'price': 1, 'quantity': 2},
{'id': 'B', 'price': 1, 'quantity': 1}
];
// Objects are easier to work with. An intermediate step to combine entries by Id.
var intermediate = list.reduce(function(a, curr) {
a[curr.id] = a[curr.id] || { id: curr.id, price: curr.price, quantity: 0}; // Copy id & price if the entry doesn't exist yet.
a[curr.id].quantity += curr.quantity; // Add quantity.
return a;
}, {});
// Map it all back to an array of objects.
var result = Object.keys(intermediate).map(function(id){
return intermediate[id];
});
console.log(result);
You can try this code, it's very simple to understand:
var objs = [
{ 'id': 'A', 'price': 1, 'quantity': 1 },
{ 'id': 'A', 'price': 1, 'quantity': 2 },
{ 'id': 'B', 'price': 1, 'quantity': 1 }
];
const results = objs.reduce(function(acc, current) {
var obj = acc.find(function(o) {
return o.id === current.id && o.price === current.price
});
if (obj) {
obj.quantity += current.quantity;
} else {
acc.push(current);
}
return acc;
}, []);
console.log(results);
maybe not efficient, but does its work.
const array = [{
'id': 'A', 'price': 1, 'quantity': 1
},{
'id': 'A', 'price': 1, 'quantity': 2
},{
'id': 'B', 'price': 1, 'quantity': 1
}];
const aggregated = array.reduce(function (p, c) {
if (!p[c.id]) p[c.id] = {quantity: c.quantity, id: c.id, price: c.price};
else p[c.id].quantity += c.quantity;
return p;
}, {});
const final = Object.keys(aggregated).map(function(k) { return aggregated[k]; });
console.log(final);

Based on one array object keys and other array object values create one more new array?

I have two arrays like this
arr1 = [
{
'name':'Victoria Cantrell',
'position':'Integer Corporation',
'office':'Croatia',
'ext':'0839',
'startDate':'2015-08-19',
'salary':208.178
},
{
'name':'Pearleeee',
'position':'In PC',
'office':'Cambodia',
'ext':'8262',
'startDate':'2014-10-08',
'salary':114.367
},
{
'name':'Pearl Crosby',
'position':'Integer',
'office':'Cambodia',
'ext':'8162',
'startDate':'2014-10-08',
'salary':114.367
}
]
arr2 =
[{
'name': 'name',
'checkfilter': false
},
{
'name': 'position',
'checkfilter': true
},
{
'name': 'office',
'checkfilter': true
},
{
'name': 'startDate',
'checkfilter': false
},
{
'name': 'ext',
'checkfilter': false
},
{
'name': 'salary',
'checkfilter': false
}]
based on checkfilter== true i want produce third array like this
arr3 = `
[{
name: 'position',
values: [{
checkName: 'Integer Corporation'
},
{
checkName: 'In PC'
},
{
checkName: 'Integer'
}]
},
{
name:'office',
values: [{
checkName: 'Croatia'
},
{
checkName: 'Cambodia'
}]
}
]
`
I tried to solve this scenario like this, but its not working perfect
arr3=[]
this.arr2.forEach((column: any) => {
if (column.ischeckFilter === true) {
this.arr3.push({
name: column.name,
values: []
});
}
});
this.arr1.forEach((d: any) => {
this.arr2.forEach((column: any) => {
if (column.ischeckFilter === true) {
this.arr3.forEach((c: any) => {
// console.log(d[column.name], c.name, 'JJJJ');
// console.log(Object.keys(d), 'BBBBBBBBB');
let keys = Object.keys(d);
keys.forEach((k: any) => {
if (k === c.name) {
if (find( c.values, { 'checkName': d[column.name]}) === undefined) {
c.values.push({
checkName: d[column.name] ,
ischeck: false
});
}
}
});
});
}
});
});
console.log( this.arr3)
}
the output array values should not contain any duplicate, I used for each loops, is there any best practices to solve this scenario like decresing the loops, because above arrays length is somuch, if i use more loops it's incresing the loading time, so please let me know how to solve this issue smartly
Thanks in advance
Fairly easy with use of standard map, filter and reduce:
var arr1 = [{
'name':'Victoria Cantrell',
'position':'Integer Corporation',
'office':'Croatia',
'ext':'0839',
'startDate':'2015-08-19',
'salary':208.178
}, {
'name':'Pearleeee',
'position':'In PC',
'office':'Cambodia',
'ext':'8262',
'startDate':'2014-10-08',
'salary':114.367
}, {
'name':'Pearl Crosby',
'position':'Integer',
'office':'Cambodia',
'ext':'8162',
'startDate':'2014-10-08',
'salary':114.367
}];
const arr2 = [{
'name': 'name',
'checkfilter': false
},{
'name': 'position',
'checkfilter': true
},{
'name': 'office',
'checkfilter': true
},{
'name': 'startDate',
'checkfilter': false
},{
'name': 'ext',
'checkfilter': false
},{
'name': 'salary',
'checkfilter': false
}];
const isDuplicate = (arr, name) => !arr.find(({ checkname }) => checkname === name);
const arr3 = arr2
.filter(({ checkfilter }) => checkfilter)
.map(({ name }) => ({
name: name,
values: arr1.reduce((names, item) => isDuplicate(names, item[name]) ?
[...names, { checkname: item[name] } ] : names, [])
}));
console.log(arr3);
function createArr3(target, source) {
for (var prop in source) {
if (source[prop]["checkfilter"]) {
var newArr3Element = {};
newArr3Element.name = source[prop].name;
newArr3Element.values = [];
target.forEach(function(element) {
if (newArr3Element.values.indexOf(element[source[prop].name]) < 0){
newArr3Element.values.push(element[source[prop].name]);
}
});
arr3.push(newArr3Element);
}
}
console.log(arr3);
}
https://jsfiddle.net/x6140nct/
First lets filter our arr2.
var x = arr2.filter((r) => r.checkfilter).
Our Name gets mapped..
map((r) => ({name:r.name,
Get our matching values., and also remove duplicates.
values:([...new Set(arr1.map((x) => (x[r.name])))]).
Finally remap, so we get {checkName: ?}
map((r) => ({checkName:r}))
Lets not forget to close the brackets & stuff..
}));
const arr1 = [
{
'name':'Victoria Cantrell',
'position':'Integer Corporation',
'office':'Croatia',
'ext':'0839',
'startDate':'2015-08-19',
'salary':208.178
},
{
'name':'Pearleeee',
'position':'In PC',
'office':'Cambodia',
'ext':'8262',
'startDate':'2014-10-08',
'salary':114.367
},
{
'name':'Pearl Crosby',
'position':'Integer',
'office':'Cambodia',
'ext':'8162',
'startDate':'2014-10-08',
'salary':114.367
}
];
const arr2 =
[{
'name': 'name',
'checkfilter': false
},
{
'name': 'position',
'checkfilter': true
},
{
'name': 'office',
'checkfilter': true
},
{
'name': 'startDate',
'checkfilter': false
},
{
'name': 'ext',
'checkfilter': false
},
{
'name': 'salary',
'checkfilter': false
}];
var x = arr2.filter((r) => r.checkfilter).
map((r) => ({name:r.name,
values:([...new Set(arr1.map((x) => (x[r.name])))]).
map((r) => ({checkName:r}))
}));
console.log(x);

javascript foreach find and update

Let say I have an array of tree type data
[ { 'id': 1, 'name': 'root', 'parent_id': 1 },
{ 'id': 2, 'name': 'level 1', 'parent_id': 1 },
{ 'id': 3, 'name': 'level 2', 'parent_id': 1 },
{ 'id': 4, 'name': 'level 2.1', 'parent_id': 3 } ]
is it possible to update the array to
[ { 'id': 1, 'name': 'root', 'parent_id': 1, 'parent_name': 'root' },
{ 'id': 2, 'name': 'level 1', 'parent_id': 1, 'parent_name': 'root' },
{ 'id': 3, 'name': 'level 2', 'parent_id': 1, 'parent_name': 'root' },
{ 'id': 4, 'name': 'level 2.1', 'parent_id': 3, 'parent_name': 'level 2' } ]
using lodash forEach and find?
_.forEach(arr, function (o) {
let item = _.find(arr, {'id': o.parent_id})
o.parent_name = item.name
})
my problem is inside the function of forEach, it has no idea what arr is, I got it working replacing the forEach with a plain for loop.
for (i = 0; i < arr.length; i++) {
let item = _.find(arr, {'id': arr[i].parent_id})
arr[i].parent_name = item.name
}
So wondering if there're any more elegant way to accomplish this
Here's another way to solve your problem using a hash to improve performance:
let hash = _.keyBy(data, 'id');
This will create an object where the keys are the id:
{
1: { id: 1, name: 'root' ... },
2: { id: 2, name: 'level 1' ... },
...
}
Then just iterate over the data to set the parent_name using the hash:
_.each(data, item => item.parent_name = _.get(hash, [item.parent_id, 'name']));
Try reading the documentation on _.forEach.
The iteratee is invoked with three arguments: (value, index|key, collection).
You need to setup your forEach like so:
_.forEach(arr, function (o, idx, arr) {
let item = _.find(arr, {'id': o.parent_id})
o.parent_name = item.name
});
you can use .map to change object as-
const arr =[ { 'id': 1, 'name': 'root', 'parent_id': 1 },
{ 'id': 2, 'name': 'level 1', 'parent_id': 1 },
{ 'id': 3, 'name': 'level 2', 'parent_id': 1 },
{ 'id': 4, 'name': 'level 2.1', 'parent_id': 3 } ];
const newArr = arr.map((item) => {
if(name === 'root') return Object.assign({}, item, {parent_name: 'root'});
const findIndexOfParent = arr.findIndex((pItem) => pItem.id == item.parent_id);
if(findIndexOfParent > -1){
return Object.assign({}, item, {parent_name: arr[findIndexOfParent].name});
}
});
console.log(newArr);

Sum value form array in object

I've an object:
var stuffData = {
'Fruit': [{
'Name': 'Apple',
'Price': '2'
}, {
'Name': 'Kiwi',
'Price': '4'
}],
'Sport': [{
'Name': 'Ball',
'Price': '10'
}, {
'Name': 'Bike',
'Price': '120'
}],
'Kitchen': [{
'Name': 'Knife',
'Price': '8'
}, {
'Name': 'Fork',
'Price': '7'
}]
}
Now i want to get sum from Price Column.
I thought about this
for (var key in stuffData)
{
// and for each key i have to add new array with sum of price or what?
// But how will I display this sum then?
// I haven't any idea how can I deal with this
}
Something like this should work, mapping the objects, and reducing to sum each one
var stuffData = {
'Fruit': [{
'Name': 'Apple',
'Price': '2'
}, {
'Name': 'Kiwi',
'Price': '4'
}],
'Sport': [{
'Name': 'Ball',
'Price': '10'
}, {
'Name': 'Bike',
'Price': '120'
}],
'Kitchen': [{
'Name': 'Knife',
'Price': '8'
}, {
'Name': 'Fork',
'Price': '7'
}]
}
var o = {};
Object.keys(stuffData).forEach(function(key) {
o[key] = stuffData[key].map(function(item) {
return parseInt(item.Price, 10);
}).reduce(function(a,b) {
return a + b;
});
});
document.body.innerHTML = '<pre>' + JSON.stringify(o, 0, 4) + '</pre>';
The result would be
{
Fruit: 6,
Sport: 130,
Kitchen: 15
}
Try like this
for (var key in stuffData) {
var totalSum =0;
if(stuffData[key] = 'Fruit'{
for(var i=0; i< stuffData[key].length ; i++){
totalSum+=stuffData[key].price;
}
}
if(stuffData[key] = 'Sport'{
for(var i=0; i< stuffData[key].length ; i++){
totalSum+=stuffData[key].price;
}
}
if(stuffData[key] = 'Kitchen'{
for(var i=0; i< stuffData[key].length ; i++){
totalSum+=stuffData[key].price;
}
}
console.log("TOTAL SUM",totalSum);
}
You can use Array.prototype.reduce to sum over a list.
var categorySums = {};
for(category in stuffData) {
var items = stuffData[category];
categorySums[category] = items.reduce(function (sum, item) {
return sum + parseInt(item.Price);
}, 0);
}
If you use the library lodash (or something similar, e.g. underscore or Ramda), they've got a mapValues utility that makes this simpler:
var categorySums = _.mapValues(stuffData, function(items) {
return _.sum(items.map(function (item) {
return parseInt(item.Price);
}));
});
Thank you for every answer. The best solution is below:
var sum = {};
for (var k in stuffData)
{
sum[k] = 0;
stuffData[k].forEach(function (e)
{
sum[k] += parseInt(e.price);
});
}

Categories

Resources