Related
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)
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)
I have an array of objects here:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
I want to add quantities of the duplicate objects together before removing them
So the result is:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test3", quantity:2 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:2 },
{ id: 6, name: "test8", quantity:1 }
];
I have seen variations of it done removing duplicates using map or reduce but I haven't seen anything that can what I want to accomplish in an eloquent way without using too many loops.
I have been thinking about how to best accomplish this all day and haven't found anything, any help would be appreciated
You can use reduce with an object to store the element with each id.
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
const res = Object.values(
arr.reduce((acc,curr)=>{
acc[curr.id] = acc[curr.id] || {...curr, quantity: 0};
acc[curr.id].quantity += curr.quantity;
return acc;
}, {})
);
console.log(res);
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 }
];
var result = arr.reduce(function (r, a) {
r[a.id] = r[a.id] || { id: a.id, quantity: 0, name: a.name };
r[a.id].quantity += a.quantity;
return r;
}, Object.create(null));
console.log(JSON.stringify(result));
Using forEach loop and build object with aggregated quantity count.
const convert = (arr) => {
const res = {};
arr.forEach(({ id, ...rest }) =>
res[id] ? (res[id].quantity += 1) : (res[id] = { id, ...rest })
);
return Object.values(res);
};
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 },
];
console.log(convert(arr));
I have the following array of objects:
[
{
id: 1,
someOtherStuff: 'abc,
Drink: { name: 'Coca-Cola', price: 2.5 }
},
{
id: 2,
someOtherStuff: 'def,
Drink: { name: 'Fanta Orange', price: 3 }
},
{
id: 3,
someOtherStuff: 'ghi,
Drink: { name: 'Sprite', price: 1.8 }
},
{
id: 6,
someOtherStuff: 'jkl,
Drink: { name: 'Coca-Cola', price: 2.5 }
},
{
id: 7,
someOtherStuff: 'mno,
Drink: { name: 'Coca-Cola', price: 2.5 }
}
]
i want to group them by duplicates like this:
[
{
count: 3,
drinkName: 'Coca-Cola',
price: 2.5
},
{
count: 1,
drinkName: 'Fanta Orange',
price: 3
},
{
count: 1,
drinkName: 'Sprite',
price: 1.8
}
]
I have tried in several ways to map a new array.But unfortunately I can't get it to work
I hope someone can help me with that
let result= {};
inputArray.map((item)=>{
let key = `${item.Drink.name}-${item.Drink.price}`;
if(result[key]){
result[key].count = result[key].count +1;
}else{
result[key] = {count:1,drinkName:item.Drink.name,price:item.Drink.price};
}
});
result = Object.values(result);
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);