Filter and combine fields from 2 arrays with ES6 JavaScript? - javascript

I have 2 arrays, one of pizza details and the other is an order state. The id field is what links them. So in this example, the order has 2 x Pepperoni and 3 x Margarita.
const pizzaContent = [
{
id: 0,
name: 'Pepperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawaiian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
I'm trying to create a new array which has the quantity from orders and the fields from pizzaContent. Any pizzas which aren't in the order shouldn't be part of this array.
I've gotten close with the following:
const pizzasInOrder = this.props.orders.map(order => {
return (
{
quantity: order.quantity,
pizza: this.props.pizzas.find(pizza => {
return (
order.id === pizza.id
);
})
}
)
});
However, the result is:
pizzasInOrder = [
{
pizza: {id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3},
quantity:2
},
{
pizza: {id: 1, name: "Margarita", price: 25, stockQuantity: 3},
quantity:3
}
]
But what I need is:
pizzasInOrder = [
{
id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3, quantity: 2
},
{
id: 1, name: "Margarita", price: 25, stockQuantity: 3, quantity: 3
}
]

Use Object.assign and no extra keys
const pizzasInOrder = this.props.orders.map(order =>
Object.assign({quantity: order.quantity},
this.props.pizzas.find(pizza => order.id === pizza.id))
);

You can use Object.assign() to merge objects into one.
example..
const pizzaContent = [
{
id: 0,
name: 'Peperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
let pizzasInOrder = orders.map((order) => {
return Object.assign(order,
pizzaContent.find(pizza => order.id === pizza.id));
});
console.log(pizzasInOrder);

Related

Question from a beginner: Unexpected JS behavior [duplicate]

I'm trying to convert an array of objects where i return duplicated objects if the object properties quantity is greater than 1.
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
// desired return
[
{ id: 1, name: "Scissor", price: 2 }
{ id: 1, name: "Scissor", price: 2 }
{ id: 1, name: "Scissor", price: 2 }
{ id: 2, name: "Hat", price: 6.5}
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
{ id: 3, name: "Socks", price: 0.5 }
]
My code:
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
let newObjects= [];
Object.entries(objects).forEach(([key, value]) => {
for (let i=0; i < value.quantity; i++){
newObjects.push({ id: value.id, name: value.name, price: value.price})
}
});
console.log(newObjects);
So my code above does work, does return what i wanted, however i feel like there is a better/smoother and more of ES6 and beyond method. Could anyone please suggest a better way?
You could use .fill() and .flatMap().
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
let newObjects = objects.flatMap(e=>
Array(e.quantity).fill({id: e.id, name: e.name, price: e.price})
);
console.log(newObjects);
You can use an array reduce along with an array fill.
The map is required only if you want to have unique references otherwise you can fill using the same object.
const objects = [
{ id: 1, name: "Scissor", price: 2, quantity: 3 },
{ id: 2, name: "Hat", price: 6.5, quantity: 1 },
{ id: 3, name: "Socks", price: 0.5, quantity: 5 },
];
const output = objects.reduce((a, c) => {
return a.concat(Array(c.quantity).fill({}).map(x=>({
id: c.id,
name: c.name,
price: c.price
})))
}, []);
console.log(output)

How to groupBy with sum using lodash

I have productId which is an array of values.
const groupByvalue= [1, 2];
I have products that have multiple product arrays.
const products = [
{
id: 1,
name: 'milk',
qty: 2
},
{
id: 2,
name: 'butter',
qty: 2
},
{
id: 3,
name: 'milk',
qty: 2
},
{
id: 2,
name: 'butter',
qty: 2
},
{
id: 1,
name: 'milk',
qty: 2
},
{
id: 3,
name: 'butter',
qty: 2
},
{
id: 1,
name: 'milk',
qty: 2
},
{
id: 3,
name: 'butter',
qty: 2
}
];
const groupByKey = 'id';
I need to group the products based on the product's id.
conditions
i)groupBy should be based on the groupByvalue with groupBykey array (only groupBy 1 , 2)
ii) after the group it should sum all the qty
expected
[
{
id: 1,
name : "milk",
qty : sum of all qty
},
{
id: 2,
name : "butter",
qty : sum of all qty
}
];
Thanks!!
Using Lodash
var _ = require('lodash');
var groupByValue = [1, 2];
var groupByKey = 'id';
const products = [
{ id: 1, name: 'milk', qty: 2 },
{ id: 2, name: 'butter', qty: 2 },
{ id: 3, name: 'milk', qty: 2 },
{ id: 2, name: 'butter', qty: 2 },
{ id: 1, name: 'milk', qty: 2 },
{ id: 3, name: 'butter', qty: 2 },
{ id: 1, name: 'milk', qty: 2 },
{ id: 3, name: 'butter', qty: 2 }
]
const ans = _(products)
.groupBy(groupByKey)
.map((product) => {
if(groupByValue.includes(product[0].id)){
return {
id: product[0].id,
name: product[0].name,
qty: _.sumBy(product, 'qty')
}
}
})
.value()
console.log(ans.filter(item => item));
Output :-
[ { id: 1, name: 'milk', qty: 6 }, { id: 2, name: 'butter', qty: 4 } ]
No need for lodash whatsoever, this is a native .reduce() task. You may do like;
var groupByValue = [1, 2],
groupByKey = 'id',
products = [ { id : 1
, name: 'milk'
, qty : 2
}
, { id : 2
, name: 'butter'
, qty : 2
}
, { id : 3
, name: 'milk'
, qty: 2
}
, { id : 2
, name: 'butter'
, qty : 2
}
, { id : 1
, name: 'milk'
, qty : 2
}
, { id : 3
, name: 'butter'
, qty : 2
}
, { id : 1
, name: 'milk'
, qty : 2
}
, { id : 3
, name: 'butter'
, qty : 2
}
],
interim = products.reduce( (r,p) => ( groupByValue.includes(p[groupByKey]) && (r[p[groupByKey]] ? r[p[groupByKey]].qty += p.qty
: r[p[groupByKey]] = Object.assign({},p))
, r
)
, {}
),
result = Object.values(interim);
console.log(result);
The milk products with id:3 doesn't count.
I would filter the array first, and then make the map,
this can be done in single .reduce(...) function, but its more readable using lodash:
const _ = require('lodash');
const groupByValue = [1, 2];
const groupByKey = 'id';
const products = [
{ id: 1, name: 'milk', qty: 1 },
{ id: 2, name: 'butter', qty: 2 },
{ id: 3, name: 'cheese', qty: 4 },
{ id: 2, name: 'butter', qty: 5 },
{ id: 1, name: 'milk', qty: 3 },
{ id: 3, name: 'cheese', qty: 6 },
{ id: 1, name: 'milk', qty: 1 },
{ id: 3, name: 'cheese', qty: 7 }
];
const result = _(products)
.filter(itm => groupByValue.includes(itm.id))
.groupBy(groupByKey)
.map(arr => {
const { id, name } = arr[0];
return { id, name, qty: _(arr).sumBy('qty') };
})
.value();
console.log(result);
// output: [{ id: 1, name: 'milk', qty: 5},{id: 2, name: 'butter', qty: 7}]

How does one order array-items of complex data-structures by a specific but deeply nested property-value?

This is my array, I have an object and then a count of how many repeats Id have for example the first object has the Id 2 repeated 3 times.
[{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]
Now I need to order this array based on an Id for example the number "9" so if the first object has the Maximus count of the id 9 of all it will be the first and the others whit minus count would be bellow, like this, the number 9 will be a random number.
[{
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]
Using Array.prototype.sort one needs to write a function which compares two array/list items according to the OP's requirements.
Such a comparator is expected to return a number value either grater than Zero or lower than Zero or Zero itself in case of item equality.
Thus one needs to find two different counts, one count for each item which will be found by searching an item's Category array by an additionally provided id value.
In order to keep the compare function reusable it is implemented as a function which allows a context to be bound to it which in the OP's case is an object that features the id one is looking for ... e.g. something like ... { id: 9 } or { id: 4 } ...
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
const bCount = b.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
As one of the comments does point out, the above code requires a version of at least node 14.0.0 due to the function compareByBoundIdCountOfItemCategoryList which uses both the Optional Chaining Operator / ?. and the Nullish Coalescing Operator / ??.
In order to let the script not break one has to replace the line ...
... aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
... with this alternative ...
... aCount = (a.Category.find(ctgry => ctgry.Id === id) || { count: -1 }).count;
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = (
a.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
const bCount = (
b.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I find this format a simpler alternative to the answer from Peter Seliger. We simply store the sought id in a closure when creating the comparator function we pass to sort:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) ?.count ?? -1) -
(c1 .find (({Id}) => Id === categoryId) ?.count ?? -1)
const input = [{Id: 1, Info: "Info", Category: [{Id: 2, count: 3}, {Id: 4, count: 1}, {Id: 8, count: 1}, {Id: 18, count: 1}, {Id: 9, count: 1}, {Id: 3, count: 1}]}, {Id: 2, Info: "Info 2", Category: [{Id: 2, count: 3}, {Id: 9, count: 2}, {Id: 21, count: 1}, {Id: 3, count: 1}]}, {Id: 3, Info: "Info 3", Category: [{Id: 4, count: 1}, {Id: 11, count: 1}, {Id: 9, count: 1}]}]
console .log (input .sort (byCategoryCount (9)))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you don't have the nullish coalescing operator available in your environment, this variant is not much worse:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}) .count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}) .count
We could also choose to write a wrapper function that returns a sorted version without mutating the original list. It might look like this:
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (byCategoryCount (categoryId))
But at that point we might start to wonder whether the helper function is offering us anything and we might choose to refactor to
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}).count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}).count
)
This should work for you sortByCount:
var my_arr = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
function sortByCount(arr, targetId){
var arr_temp = [];
arr.forEach(el => {
var elem = el.Category.filter(e => e.Id === targetId)[0];
var value = elem ? elem.count : -1;
arr_temp.push({
value: value,
obj: el
});
});
arr_temp.sort((a,b)=> b.value - a.value);
return arr_temp.map(el => el.obj);
}
var sortedArr = sortByCount(my_arr, 9);
console.log(sortedArr)

return specific properties from array of objects into new object [duplicate]

This question already has answers here:
From an array of objects, extract value of a property as array
(24 answers)
Closed 2 years ago.
I've an array of objects, what I want is to copy all the objects from that, but with specific properties not all the properties.
like for example I've this object named cart
cart = [
{ id: 1, name: 'makeup', price: 200, qty: 1 },
{ id: 2, name: 'gloves', price: 300, qty: 2 },
{ id: 3, name: 'sanitizer', price: 400, qty: 3 },
{ id: 4, name: 'book', price: 100, qty: 1 },
{ id: 5, name: 'hairs', price: 250, qty: 4 },
{ id: 6, name: 'soap', price: 50, qty: 5 },
{ id: 7, name: 'shampoo', price: 700, qty: 1 },
]
and I want to extract only the id and qty attributes to a new array of objects.
How do I do this.
I already tried
products=cart.map(prod=>prod.id, prod.qty)
but this doesn't seems to be working.
Thanks in advance to helping hands
You can Array.prototype.map() or Array.prototype.reduce() over the entire array and only return the values you want.
const cart = [
{ id: 1, name: 'makeup', price: 200, qty: 1 },
{ id: 2, name: 'gloves', price: 300, qty: 2 },
{ id: 3, name: 'sanitizer', price: 400, qty: 3 },
{ id: 4, name: 'book', price: 100, qty: 1 },
{ id: 5, name: 'hairs', price: 250, qty: 4 },
{ id: 6, name: 'soap', price: 50, qty: 5 },
{ id: 7, name: 'shampoo', price: 700, qty: 1 },
]
console.log( cart.map( elem => ({id:elem.id, qty : elem.qty})))
You need to iterate and return only the desired properties.
cart = [
{ id: 1, name: 'makeup', price: 200, qty: 1 },
{ id: 2, name: 'gloves', price: 300, qty: 2 },
{ id: 3, name: 'sanitizer', price: 400, qty: 3 },
{ id: 4, name: 'book', price: 100, qty: 1 },
{ id: 5, name: 'hairs', price: 250, qty: 4 },
{ id: 6, name: 'soap', price: 50, qty: 5 },
{ id: 7, name: 'shampoo', price: 700, qty: 1 },
]
const newcart = cart.map(item => {
return {id: item.id, qty: item.qty}
});
console.log(newcart)
You almost had it correct.
When using arrow functions without the brackets, whatever is put after the arrow function is returned.
So your code could look like this:
const products = cart.map(({ id, qty }) => ({ id, qty }));
We destructure the object in the arrow function and return it as a new object.
Make sure to have the round brackets around the value that you return. Otherwise javascript will see it as the body of a function instead of an object that is returned.
You can update your .map() method like this to acheive the desired result:
const cart = [{id:1,name:"makeup",price:200,qty:1},{id:2,name:"gloves",price:300,qty:2},{id:3,name:"sanitizer",price:400,qty:3},{id:4,name:"book",price:100,qty:1},{id:5,name:"hairs",price:250,qty:4},{id:6,name:"soap",price:50,qty:5},{id:7,name:"shampoo",price:700,qty:1}];
const products = cart.map(({id, qty}) => ({ id, quantity: qty }))
console.log(products)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Moving data from 2 arrays of objects into a third array

so assume i have 2 arrays of objects...
let orders = [
{ id: 1, itemName: 'Peaches', amount: 2 },
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
];
let items = [
{ id: 1, name: 'Peaches', qty: 10 },
{ id: 2, name: 'Mangoes', qty: 3 }
];
and i want to find the list of orders for every item and put them in an array called linkedOrders, I tried the below code:
let linkedOrders = _.map(items, item => _.where(orders, { name: item.name }));
console.log(linkedOrders);
This is what I am getting:
[{ id: 1, itemName: 'Peaches', amount: 2 }],
[{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }]
but I want something like this:
[{'Peaches': [
{ id: 1, itemName: 'Peaches', amount: 2 }
],
'Mangoes': [
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
]],
Any help would be appreciated thanks.
You can simply do that using Array reduce method.
const result = items.reduce((result, item) => {
result.push({
[item.name]: orders.filter((order) => order.itemName === item.name)
});
return result;
}, []);
For more information on reduce, check Array​.prototype​.reduce()

Categories