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);
Related
Is there a way to refactor this so the code look shorter and readable? There won't be more than 100 Ids in the payload, so I think the performance does not matter in this case.
const payload = {
RequestIds: [
{ Id: 1 },
{ Id: 3 },
]
}
const result = {
Items: [
{ Id: 1, Name: 'Item 1 A'},
{ Id: 1, Name: 'Item 1 B'},
{ Id: 2, Name: 'Item 2 C'},
{ Id: 2, Name: 'Item 2 D'},
{ Id: 3, Name: 'Item 3 E'},
{ Id: 3, Name: 'Item 3 F'},
]
}
const items = [];
for(const payloadItem of payload.RequestIds) {
const filteredItems = result.Items.filter(item => item.Id === payloadItem.Id);
if (filteredItems.length > 0) {
items.push(...filteredItems);
}
}
console.log(items);
Make a Set of the Ids in the payload, then do a single .filter by whether the Id being iterated over is included:
const payload = {
RequestIds: [
{ Id: 1 },
{ Id: 3 },
]
};
const payloadIds = new Set(payload.RequestIds.map(({ Id }) => Id));
const result = {
Items: [
{ Id: 1, Name: 'Item 1 A'},
{ Id: 1, Name: 'Item 1 B'},
{ Id: 2, Name: 'Item 2 C'},
{ Id: 2, Name: 'Item 2 D'},
{ Id: 3, Name: 'Item 3 E'},
{ Id: 3, Name: 'Item 3 F'},
]
}
const items = result.Items.filter(({ Id }) => payloadIds.has(Id));
console.log(items);
Please try this.
const resultItems = result.Items;
const items = payload.RequestIds.reduce((prev, cur) => [...prev, ...resultItems.filter(item => item.Id === cur.Id)], []);
or
const items = resultItems.filter(item => payload.RequestIds.some(req => req.Id === item.Id));
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>
Got a bit of a puzzle here...I want to loop through allItems and return allItems but replace with any newItems that matches its id. How can I look for a match on id and then replace it with the correct object into the array?
const allItems = [
{
'id': 1,
'category_id': 1,
'text': 'old',
},
{
'id': 2,
'category_id': 1,
'text': 'old'
}
]
const newItems = [
{
'id': 1,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
},
{
'id': 2,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
}
]
What I tried so far:
for(let i = 0; i < allItems.length; i++) {
if(newItems.indexOf(allItems[i].id) > -1){
allItems[i] = newItems
}
}
How can I get the position of the object in newItems and then replace it into allItems?
Use Array.map and Array.find():
const allItems = [
{ 'id': 1, 'category_id': 1, 'text': 'old' },
{ 'id': 2, 'category_id': 1, 'text': 'old' }
];
const newItems = [
{ 'id': 1, 'category_id': 1, 'text': 'new', 'more_info': 'abcd' },
{ 'id': 2, 'category_id': 1, 'text': 'new', 'more_info': 'abcd' }
];
const result = allItems.map(x => {
const item = newItems.find(({ id }) => id === x.id);
return item ? item : x;
});
console.log(result);
This can even be shortened by using a logical or to return the original item when the call to find returns undefined:
const result = allItems.map(x => newItems.find(({ id }) => id === x.id) || x);
Regarding your code, you can't use indexOf since it only compares primitive values or references in the case of arrays and objects.
Just use map like so:
const allItems = [{
'id': 1,
'category_id': 1,
'text': 'old',
},
{
'id': 2,
'category_id': 1,
'text': 'old'
}
];
const newItems = [{
'id': 1,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
},
{
'id': 2,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
}
];
const replacedItems = allItems.map(e => {
if (newItems.some(({ id }) => id == e.id)) {
return newItems.find(({ id }) => id == e.id);
}
return e;
});
console.log(replacedItems);
Just using a simple Array.map and a method to check the other array.
const allItems = [
{
'id': 1,
'category_id': 1,
'text': 'old',
},
{
'id': 2,
'category_id': 1,
'text': 'old'
},
{
'id': 3,
'category_id': 1,
'text': 'old_same'
}
]
const newItems = [
{
'id': 1,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
},
{
'id': 2,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
}
]
const findNewItem = (oldItem) => newItems.find(item => item.id === oldItem.id);
let arr = allItems.map(item => findNewItem(item)||item);
console.log(arr);
Depending on how large your input arrays are, you might consider adding an intermediate step to build a "map" of your newItems, where the key of this map is the id of the item.
Using a mapping such as this would allow for much faster reconciliation (and replacement) of items from the allItems array with items in the newItems array:
function replaceItemsOnId(items, replacement) {
/*
Create a map where the key is the id of replacement items.
This map will speed up the reconciling process in the
subsequent "map" stage
*/
const replacementMap = replacement.reduce((map, item) => {
map[ item.id ] = item
return map;
}, {})
/*
Map the items to a new array where items in the result array
are either clones of the orignals, or replaced by items of
"replacement" array where thier id matches the item being mapped
*/
return items.map(item => {
const use = replacementMap[ item.id ] || item
return { ...use }
})
}
const allItems = [
{
'id': 1,
'category_id': 1,
'text': 'old',
},
{
'id': 2,
'category_id': 1,
'text': 'old'
}
]
const newItems = [
{
'id': 1,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
},
{
'id': 2,
'category_id': 1,
'text': 'new',
'more_info': 'abcd'
}
]
console.log(replaceItemsOnId(allItems, newItems))
You could get a reference to the object you are looking for by id but that would not be enough because then you would need the index in order to replace it in allItem (allItem[index] = newItem), so I suggest finding that index instead first like this:
for (let item of newItems) {
let indexNewItem = allItems.map(function (e) { return e.id }).indexOf(item.id);
allItems[indexNewItem] = item
}
this should work
The answers for this question mostly have the code iterating through all the items in the original array to see which should be replaced. If the original array is large, and you don't need to replace many, it might be better performance to change the logic. I only needed to replace one item in an array (customers). I did it like this:
const index = customers.findIndex(x => x.Id == editedCust.Id);
if (index >= 0)
customers[index] = editedCust;
You could iterate through all the items in newItems and do the above for each.
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);
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)