Merge duplicated objects in javascript array with their appearance frequence - javascript

i have a javascript array who look like this
[
{ quantity: 1, name: 'Menu sandwichs' },
{ quantity: 1, name: 'Menu sandwichs' },
{ quantity: 1, name: 'Menu sandwichs' },
{ quantity: 1, name: 'Pizza' },
{ quantity: 1, name: 'Piza' }
]
and i want to get this array
[
{ quantity:3, name:'Menu Sandwich' },
{ quantity:2, name:'Pizza' }
]
Could you hel me please ?

Assuming the quantities in the original array may sometimes be something other than 1:
const mergeQuantities = (values) => {
const result = {}
values.forEach(({ quantity, name }) => {
result[name] = result[name] || { name, quantity: 0 }
result[name].quantity += quantity
})
return Object.values(result)
}
This creates a new object where the attribute names are the name from each thing in the array. Then iterates over the list, adding each quantity to the result. Finally, it discards the keys, leaving just the array in the form you want.

Related

react js useState update the quantity

I am new to react. I have got an issue updating a quantity.
const[persons,setPersons] = useState(personLists)
if you console.log(persons) it will give output as below. Now I want to
update qty for particular misc index of particular person index .
I have two persons in below array and each person have further two misc array
[
{
id: 1,
name: "john",
gender: "m",
misc: [
{
id: 1,
name: "xxx",
qty: 1
},
{
id: 2,
name: "xxx1",
qty: 1
}
]
},
{
id: 2,
name: "mary",
gender: "f",
misc: [
{
id: 1,
name: "aaa",
qty: 1
},
{
id: 2,
name: "bbb",
qty: 1
}
]
},
]
Now i want to update qty under misc array for that particular person.I have a function that takes the index of person array and index of misc array as below.
const updatePersonMiscQty = (personIndex, miscIndex) => {
setPersons(persons =>
persons.map((person,key) => {
const found = person.misc.find(d => key === miscIndex);
if (found) {
found.qty += 1;
}
return person;
})
}
let say my personIndex is 0 and miscIndex= is 1
so when usse click the button It should look into first person array, go to second index of misc and update qty.
I am looking for a solutions
access the items by indexes and update
setPersons(persons => {
const miscItem = persons[personIndex]?.misc?.[miscIndex]
if (miscItem ) {
miscItem.qty += 1;
}
return [...persons];
}
})

Remove one element at index from the list inside an array of objects

I would like to remove one element at index from the list items inside an array of objects using react.
For now, I do not know which method to use.
const [users, setUsers] = useState([
{
username: "Peter",
items: [
{ name: "Bananas 🍌", quantity: 10 },
{ name: "Strawberry 🍓", quantity: 20 },
{ name: "Milk 🥛", quantity: 6 },
],
},
{
username: "William",
items: [
{ name: "Brocolis 🥦", quantity: 3 },
{ name: "Carottes 🥕", quantity: 10 },
{ name: "Honey 🍯", quantity: 2 },
],
},
]);
// Remove element from list item (Pseudo Code)
function removeItem(index: number) {
const initialState = [...users]
initialState[userIndex].items.splice(itemIndex, 1)
setUsers(initialState)
}
Thank you for your help.
Assuming you want to remove item with index: itemIndex from user with index userIndex user.
function removeItem(userIndex, itemIndex) {
return users.map((x, i) => {
if (i === userIndex) return {
...x,
items: x.items.filter((y, j) => j !== itemIndex)
}
return x;
})
}
You have to do it in immutable way since it comes from state.
You can use Array.prototype.filter for that
function removeItem(index: number) {
setUsers(users.filter((user, userIndex) => userIndex !== index))
}
You can read more about Array.prototype.filter here

Filter inside filter in JavaScript

I need to filter some data inside an array of objects which is contained in another array of objects. Here is the sample structure of my data. I need to filter on categories.
[
{
id: 540,
name:'Makeup kit'
slug:'makeup-kit',
status:'publish',
categories: [
{
id: 42, name:'Fashion',slug:'fashion'
},
{
id: 43, name:'Beauty',slug:'beauty'
}
]
},
{
id: 541,
name:'Silicon gloves'
slug:'silicon-gloves',
status:'publish',
categories: [
{
id: 44, name:'Health',slug:'health'
}
]
},
{
id: 650,
name:'Julep Mask'
slug:'julep-mask',
status:'publish',
categories: [
{
id: 43, name:'Beauty',slug:'beauty'
}
]
}
]
Here is how I'm trying
beautyProducts=temp1.filter(product=>product.categories.filter(cat=>cat.id===43))
but my solution doesn't seem to work.
Array#filter() expects the function you give it to return a truthy or falsy value. Elements for which the function returns a truthy value are kept in the new array, and those that give a falsy value are removed.
You want to keep only elements for which one of the categories has an id of 43. Using a second filter, then, makes no sense here: it returns an array, and arrays are always truthy; therefore the first filter will always receive an array for each element and all elements are kept in the new array.
Instead of a second filter, you should use Array#some() - you want to know if any of the categories have id===43, and if none of them do, then you want a falsy value so that the product gets excluded from the results.
Simple change:
beautyProducts = temp1.filter(product => product.categories.some(cat => cat.id === 43))
Here is a working sample:
let temp1 = [{id:540,name:'Makeup kit',slug:'makeup-kit',status:'publish',categories:[{id:42,name:'Fashion',slug:'fashion'},{id:43,name:'Beauty',slug:'beauty'}]},{id:541,name:'Silicon gloves',slug:'silicon-gloves',status:'publish',categories:[{id:44,name:'Health',slug:'health'}]},{id:650,name:'Julep Mask',slug:'julep-mask',status:'publish',categories:[{id:43,name:'Beauty',slug:'beauty'}]}];
let beautyProducts = temp1.filter(product => product.categories.some(cat => cat.id === 43));
console.log(beautyProducts);
Try like this.
beautyProducts = temp1.map(({categories, ...others}) => {
const filteredCategories = categories.filter(cat => cat.id === 43);
return {
filteredCategories,
...others
};
}).filter(product => product.categories.length > 0)
So first, you should do the inner filter first and map the inner filtered data to the current one and do the main filter after that like above.
let data = [
{
id: 540,
name: 'Makeup kit',
slug: 'makeup-kit',
status: 'publish',
categories: [
{
id: 42, name: 'Fashion', slug: 'fashion'
},
{
id: 43, name: 'Beauty', slug: 'beauty'
}
]
},
{
id: 541,
name: 'Silicon gloves',
slug: 'silicon-gloves',
status: 'publish',
categories: [
{
id: 44, name: 'Health', slug: 'health'
}
]
},
{
id: 650,
name: 'Julep Mask',
slug: 'julep-mask',
status: 'publish',
categories: [
{
id: 43, name: 'Beauty', slug: 'beauty'
}
]
}
];
let beautyProducts = data.map(product => {
const categories = product.categories.filter(cat => cat.id === 43);
if (categories.length) {
return { ...product, categories };
}
return null;
}).filter(p => p);
console.log("Prod:", beautyProducts);
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>");
let beautyProductsTwo = data.filter(product => product.categories.some(cat => cat.id === 43));
console.log("Prod ans two:", beautyProductsTwo);

Reduce, flatten and transform nested array

I am trying to find a solution to get a specific format for my nested JSON array. I have tried to use Lodash groupBy and Javascript reduce but I can not seem to find a solution.
Input format:
[
{
lineitem: {
id:1,
price: {
amount:100
},
supplierName:TestSupplier1
},
quantity:10
},
{
lineitem: {
id:2,
price: {
amount:200
},
supplierName:TestSupplier2
},
quantity:20
},
{
lineitem: {
id:3,
price: {
amount:300
},
supplierName:TestSupplier1
},
quantity:30
},
{
lineitem: {
id:4,
price: {
amount:400
},
supplierName:TestSupplier4
},
quantity:40
},
]
Desired output format:
[
{
TestSupplier1: [
{
id:1,
amount:100,
quantity:10
},
{
id:3,
amount:300,
quantity:30
}
],
TestSupplier2: [
{
id:2,
amount:200,
quantity:20
}
],
TestSupplier4: [
{
id:4,
amount:400,
quantity:40
}
]
]
As I see it there are 3 steps here:
Group by supplierName
Flatten the price object
Move quantity into lineitem
Can anyone help solve this?
Update 1
After succesfully grouped by supplierName I now want to flatten the array further. Such that the TestSupplier'X' is placed within each array.
See example below:
[
{
supplierName:TestSupplier1,
id:1,
amount:100,
quantity:10
},
{
supplierName:TestSupplier1,
id:3,
amount:300,
quantity:30
},
{
supplierName:TestSupplier2,
id:2,
amount:200,
quantity:20
},
{
supplierName:TestSupplier4,
id:4,
amount:400,
quantity:40
}
]
If you're working in TypeScript, the following might help:
interface IInput {
lineitem: {
id: number;
price: {
amount: number;
}
supplierName: string;
}
quantity: number;
}
interface IOutputA {
[key: string]: {
id: number;
amount: number;
quantity: number;
}[];
}
interface IOutputB {
supplierName: string;
id: number;
amount: number;
quantity: number;
}
function transformA(input: IInput[]): IOutputA {
const ret: IOutputA = {};
input.forEach(it => {
ret[it.lineitem.supplierName] = [];
ret[it.lineitem.supplierName].push({
id: it.lineitem.id,
amount: it.lineitem.price.amount,
quantity: it.quantity,
}
}
return ret;
}
function transformB(outputA: IOutputA): IOutputB {
const ret: IOutputB = [];
Object.keys(outputA).forEach(x => {
outputA[x].forEach(y => {
ret.push({
supplierName: x,
id: y.id,
amount: y.amount,
quantity: y.quantity,
});
});
});
return ret;
}
It would be nice to see your attempt at solving this as well.
This should get you pretty much what you want, the only thing to note is that you can't have an object with multiple keys that are the same, so you have an array of objects rather than having an object with several keys of lineItem:
const result = data.reduce((result, item) => {
// Get the supplier name, id, amount and quantity of the current item
const supplierName = item.lineitem.supplierName;
const {
id,
price: { amount }
} = item.lineitem;
const { quantity } = item;
const lineItem = {
id,
amount,
quantity
};
// If it already exists in the result, add the data to the existing key
if (Object.keys(result).includes(supplierName)) {
return Object.assign({}, result, {
[supplierName]: [...result[supplierName], lineItem]
});
} else {
// Otherwise create a new key
return Object.assign({}, result, { [supplierName]: [lineItem] });
}
}, {});
This gives you:
{ TestSupplier1:
[ { id: 1, amount: 100, quantity: 10 },
{ id: 3, amount: 300, quantity: 30 } ],
TestSupplier2: [ { id: 2, amount: 200, quantity: 20 } ],
TestSupplier4: [ { id: 4, amount: 400, quantity: 40 } ] }
In the original question you asked for the whole thing to be wrapped in an array, which is obviously as simple as [result].
let resObj = {};
data.forEach( el => {
if(!Object.keys(resObj).includes(el.lineitem.supplierName))
{
console.log(el);
resObj[el.lineitem.supplierName] = [];
}
let newInnerObj = { id: el.lineitem.id, amount: el.lineitem.price.amount, quantity: el.quantity };
resObj[el.lineitem.supplierName].push(newInnerObj);
});
console.log(resObj);
Here is a simple answer, but like I said in my comment- you can't have object with properties of the same name, so the result is an object with TestSupplierX as properties, and each property si an array of object with the structure you requested.
It is really as simple as moving through all the original array and just construct the right object. What I think you missed is how to get them into the correct property name which comes from supplierName, and this is done just by checking if the result object already has it with Object.keys(obj).includes(key), and if it doesn't- create it simply by accessing it and initializing it to an empty array.

Creating a shopping list from an array of ingredients (formatted as objects)

var ingredients = [
{ name: 'potatoes', quantity: 4 },
{ name: 'butter', quantity: 1 },
{ name: 'milk', quantity: 1, description: '1 cup' },
{ name: 'potatoes', quantity: 3 },
{ name: 'oil', quantity: 1, description: '3 cups' } ];
const shoppingList = [];
for (let i = 0; i < ingredients.length; i ++) {
for (let j = 0; j < shoppingList.length; j ++){
let ingredient = ingredients[i];
let shoppingListItem = shoppingList[j];
if(ingredient === shoppingListItem){
break;
}else if (roughDraftItem.name === shoppingListItem.name){
shoppingListItem.quantity += roughDraftItem.quantity;
} else {shoppingList.push(roughDraftItem);
}
}
}
When I run this code the shoppingList array comes back empty. When I take out the second loop the code doesn't have a problem and I get what I need
shoppingListItem = { name: 'potatoes', quantity: 1}
It seems to be a problem of trying to compare the Ingredients array to the shoppingList array (after an object has been added).
Your shoppingList is empty so its length = 0. The second loop of the array doesn't run, since it's told to run 0 times.
You don't need the second loop to add an object to the shoppingList, so I would remove it.
As others have said, shoppingList starts with a length of 0, so the 2nd loop will never run. Also, if you're trying to sum the quantity of items with the same name, you could use reduce to simplify things:
const ingredients = [
{ name: 'potatoes', quantity: 4 },
{ name: 'butter', quantity: 1 },
{ name: 'milk', quantity: 1, description: '1 cup' },
{ name: 'potatoes', quantity: 3 },
{ name: 'oil', quantity: 1, description: '3 cups' } ];
const result = ingredients.reduce((acc, curr) => {
const exists = acc.find(item => item.name === curr.name);
if (exists) {
exists.quantity += curr.quantity;
return acc;
}
return [...acc, curr]
}, []);
console.log(result);
You can use Array.prototype.reduce and ES6 object destructuring assignment to make an aggregation of ingredients by their name, and Array.prototype.map to generate the desired output:
This solution is more declarative than nested for loops and can work with any amount of repetitive items:
var ingredients = [
{ name: 'potatoes', quantity: 4 },
{ name: 'butter', quantity: 1 },
{ name: 'milk', quantity: 1, description: '1 cup' },
{ name: 'potatoes', quantity: 3 },
{ name: 'oil', quantity: 1, description: '3 cups' }
];
// Aggregate `quantity` by `name`
var dataObj = ingredients.reduce((all, {name, quantity}) => {
all[name] = (all[name] || 0) + quantity;
return all;
}, {});
// Generate the result
var shoppingList = Object.keys(dataObj).map(ing => ({name: ing, quantity: dataObj[ing]}));
console.log(shoppingList);

Categories