Related
This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed 1 year ago.
I have two arrays:
Array 1:
[
{
name: 'Bob',
traits: {
id: 1
}
}, {
name: 'Karl',
traits: {
id: 2
}
}, {
name: 'Joseph',
traits: {
id: 3
}
}
]
Array 2:
[
{
name: 'Karl',
user_id: 2,
dog: 'Rottweiler'
}, {
name: 'Joseph',
user_id: 3,
dog: 'Poodle'
}, {
name: 'Bob',
user_id: 1,
dog: 'Puppy'
}
]
Desired outcome:
I want to be able to merge the second array into the first array by finding what element user_id matches with id and then adding the object to array.
For example:
array 1 obj
{
name: 'Bob',
traits: {
id: 1
}
}
Since the id matches with array 2 obj user_id:
{
name: 'Bob',
user_id: 1,
dog: 'Puppy'
}
Final outcome will be:
{
name: 'Bob',
traits: {
name: 'Bob',
user_id: 1,
dog: 'Puppy'
}
}
arr2.forEach((obj) => {
const idx = arr1.findIndex((o) => o.traits.id === obj.user_id);
if (idx !== -1) {
arr1[idx] = { ...arr1[idx], traits: { ...obj } }
}
})
console.log(arr1[0]) // { name: 'Bob', traits: { name: 'Bob', user_id: 1, dog: 'Puppy' } }
Turn the second array into a map keyed by user_id, and then iterate the first array. Find the corresponding object in the map, and spread the matching object value into the traits property:
let arr1 = [{name: 'Bob',traits: {id: 1}},{name: 'Karl',traits: {id: 2}},{name: 'Joseph',traits: {id: 3}}];
let arr2 = [{name: 'Karl', user_id: 2,dog: 'Rottweiler'},{name: 'Joseph', user_id: 3,dog: 'Poodle'},{name: 'Bob',user_id: 1,dog: 'Puppy'}];
let map = new Map(arr2.map(item => [item.user_id, item]));
let result = arr1.map(item => {
let traits = map.get(item.traits.id);
return traits ? { ...item, traits} : item;
});
console.log(result);
As lookup in a map has an amortised time complexity of O(1), this is more efficient than finding the key in the array on every iteration (like with calling find).
You can easily achieve this result using map and find. Just map over the first array and find the element with obj.traits.id in the arr2. then return the desired result.
const arr1 = [
{
name: "Bob",
traits: {
id: 1,
},
},
{
name: "Karl",
traits: {
id: 2,
},
},
{
name: "Joseph",
traits: {
id: 3,
},
},
];
const arr2 = [
{
name: "Karl",
user_id: 2,
dog: "Rottweiler",
},
{
name: "Joseph",
user_id: 3,
dog: "Poodle",
},
{
name: "Bob",
user_id: 1,
dog: "Puppy",
},
];
const result = arr1.map((obj) => {
const { name, traits } = obj;
const isExist = arr2.find((o) => o.user_id === traits.id);
if (isExist) {
return { name, traits: { ...isExist } };
}
return obj;
});
console.log(result);
let a = [
{
name: 'Bob',
traits: {
id: 1
}
}, {
name: 'Karl',
traits: {
id: 2
}
}, {
name: 'Joseph',
traits: {
id: 3
}
}
];
let b = [
{
name: 'Karl',
user_id: 2,
dog: 'Rottweiler'
}, {
name: 'Joseph',
user_id: 3,
dog: 'Poodle'
}, {
name: 'Bob',
user_id: 1,
dog: 'Puppy'
}
];
a.map(aobj =>{
let sameIdObj = b.find( bobj => bobj.user_id === aobj.traits.id )
sameIdObj && (aobj.traits = sameIdObj)
})
console.log(a);
How can I merge my array of objects with same value? I have got orders array, which may have the same product. If so, I want to merge them and add the quantity.
var orders = [
{
product: "chair",
quantity: 5,
price: 900,
},
{
product: "chair",
quantity: 2,
price: 900,
},
]
Expected output:
orders = [
{
product: "chair",
quantity: 7,
price: 900,
}
]
Goal: Group object array by product and add the quantity.
Here is one of the performant way to achieve that:
var orders = [
{
product: "chair",
quantity: 5,
price: 900,
},
{
product: "chair",
quantity: 2,
price: 900,
},
];
const resultTest = {};
const result = [];
orders.forEach((item) => {
if (resultTest[item.product]) {
const index = resultTest[item.product] -1;
const foundItem = result[index];
const newValue = {
...foundItem,
quantity: foundItem.quantity + item.quantity,
};
result[index] = newValue;
} else {
resultTest[item.product] = result.length + 1;
result.push(item);
}
});
console.log(result);
I think you can do this like ths
var orders = [
{
product: "chair",
quantity: 5,
price: 900,
},
{
product: "chair",
quantity: 2,
price: 900,
},
]
var output=new Array;
orders.forEach(elem=>{
var found =false;
for(var i =0; i<output.length;i++)
{
if(output[i].product==elem.product)
{
output[i].quantity+=elem.quantity;
var found = true;
break;
}
}
if(!found)
{
output.push(elem);
}
})
console.log(output);
Here is my solution in more cleaner way:
var orders = [
{
product: "chair",
quantity: 5,
price: 900,
},
{
product: "chair",
quantity: 2,
price: 900,
},
{
product: "table",
quantity: 1,
price: 1000,
}
]
const combineSimilarOrders = {};
orders.forEach((order) => {
if (!(order.product in combineSimilarOrders)) {
combineSimilarOrders[order.product] = [ { ...order } ]
}
else {
if (order.product in combineSimilarOrders) {
let product = Object.keys(combineSimilarOrders);
combineSimilarOrders[product][0].quantity += order.quantity;
combineSimilarOrders[product][0].price += order.price;
}
else {
combineSimilarOrders[order.product].push(order);
}
}
});
console.log(combineSimilarOrders);
Output:
{
chair: [ { product: 'chair', quantity: 7, price: 1800 } ],
table: [ { product: 'table', quantity: 1, price: 1000 } ]
}
I have an object that contains a few key value pairs, one of which is an array. I am trying to find the index of said array, where the 'options' properties matches the options properties of another object. The function I have attached works properly with just the product ID, but it does not work when trying to compare the options object with the newObj.options.
The Object
parsedCart = {
id:1592539903073,
products: [{
productID: 1001,
name: 'Test Product'
qty: 1,
options: {
Color: "Red",
}
}]
}
The Object I wish to compare to
newObj = {
productID: 1001,
name: 'Test Product',
qty: 1,
options: {
Color: "Red",
}
}
The Function
function productExists(newObj) {
return (
newObj.productID === productID &&
JSON.stringify(newObj.options) === JSON.stringify(options)
);
}
let exists = parsedCart.products.findIndex(productExists);
console.log(exists);
I understand that you want to find the index of the products in parsedCart according to the data in newObj. And created a new items in parsedCart options for the example
If i have interpreted your question correctly i think the reason for the error is that you try to use the properties productID and options in the context of parsedCart.products in findIndex
Please try the following example
const parsedCart = {
id: 1592539903073,
products: [
{
productID: 1001,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
{
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
],
};
const newObj = {
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
};
const index = parsedCart.products.findIndex(
(product) =>
newObj.productID === product.productID &&
newObj.options.Color === product.options.Color
);
console.log(index);
Update 0
Compare options using JSON.stringify()
const parsedCart = {
id: 1592539903073,
products: [
{
productID: 1001,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
{
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
],
};
const newObj = {
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
};
const index = parsedCart.products.findIndex(
(product) =>
newObj.productID === product.productID &&
JSON.stringify(newObj.options) === JSON.stringify(product.options)
);
console.log(index);
I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}
I have an object data as follows:
[
{
name: "Green Tea Brownie",
price: 60,
amount: 10,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
},
{
name: "Cocoa Chiffon",
price: 20,
amount: 50,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
},
{
name: "Milky Donut",
price: 40,
amount: 100
seller: {
seller_id: 421,
seller_name: "Sweet Bakery"
}
}
]
So I want to group data by "seller_id" and merge top level data assigns to object name "orders", just look like as following:
[
{
seller_id: 124,
seller_name: "Merry Shop",
orders: [
{
name: "Green Tea Brownie",
price: 60,
amount: 10
},
{
name: "Cocoa Chiffon",
price: 20,
amount: 50
}
]
},
{
seller_id: 421,
seller_name: "Sweet Bakery",
orders: [
{
name: "Milky Donut",
price: 40,
amount: 100
}
]
}
]
I tried to solve this problem several hours ago. Can anyone solve this case?
Thank you
You can use _.groupBy() and then _.map() the groups to requested format:
const { flow, partialRight: pr, groupBy, map, first, omit } = _
const fn = flow(
pr(groupBy, 'seller.seller_id'),
pr(map, group => ({
...first(group).seller,
orders: map(group, pr(omit, 'seller'))
}))
)
const data = [{"name":"Green Tea Brownie","price":60,"amount":10,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Cocoa Chiffon","price":20,"amount":50,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Milky Donut","price":40,"amount":100,"seller":{"seller_id":421,"seller_name":"Sweet Bakery"}}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If you like to do with plain JS you can use reduce
const data = [{"name":"Green Tea Brownie","price":60,"amount":10,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Cocoa Chiffon","price":20,"amount":50,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Milky Donut","price":40,"amount":100,"seller":{"seller_id":421,"seller_name":"Sweet Bakery"}}]
const output = data.reduce((op,{name,price,amount,seller})=>{
if(op[seller['seller_id']]){
op[seller['seller_id']]['orders'].push({name,price,amount})
}
else {
op[seller['seller_id']] = {
seller_name: seller.seller_name,
seller_id: seller.seller_id,
orders: [{name,price,amount}]
}
}
return op;
},{})
console.log(Object.values(output))
Here is a pure JS solution using reduce:
const orders = [{
name: "Green Tea Brownie",
price: 60,
amount: 10,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
}, {
name: "Cocoa Chiffon",
price: 20,
amount: 50,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
}, {
name: "Milky Donut",
price: 40,
amount: 100,
seller: {
seller_id: 421,
seller_name: "Sweet Bakery"
}
}];
const ordersBySeller = Object.values(orders.reduce((accum, { name, price, amount, seller: { seller_id, seller_name } }) => {
const sellerOrders = accum[seller_id] ? accum[seller_id].orders || [] : [];
accum[seller_id] = { seller_id, seller_name, orders: [...sellerOrders, { name, price, amount } ] };
return accum;
}, {}));
console.log(ordersBySeller);