add qty for similar json objects in javascript - javascript

I want the result to be summing all the qty of same cat.
var data = [
{ cat: 'EK-1',name:"test",info:"mat", quantity: 3},
{ cat: 'EK-2', name:"test2",info:"nat"quantity: 1}
];
I tried like this below i have array of object having some similar objects. how to add qty and create unque objects .below i have given what i tried.
var data = [{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-2',
name: "test2",
info: "nat",
quantity: 1
}
];
const products = Array.from(data.reduce((acc, {
cat,
quantity
}) =>
acc.set(cat, (acc.get(cat) || 0) + quantity),
new Map()
), ([cat, quantity]) => ({
cat,
quantity
}));
console.log(products);

You can do this using Array#reduce, using the accumulator to pass on the new object:
var data = [ { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-2", name: "test2", info: "nat", quantity: 1, }, ];
let seen = [];
const res = data.reduce((acc, { cat, ...rest }) => {
const idx = seen.indexOf(cat);
if (idx == -1) (acc.push({cat, ...rest}), seen.push(cat));
else acc[idx].quantity++;
return acc;
}, []);
console.log(res);

Related

How can I see if Object Array has elements in Another Object Array?

Is there a way to tell if an object array has any common elements to another object array, and what that object intersect is? (like a Contains function). In the example below,ProductId3 in Object Array 1, is also contained in Object Array 2.
I'm thinking of using a double for loop . However is there a more efficient/optimal way, or shorthand ecma or lodash function?
We are checking all object members, not just ProductId.
array1.forEach(arr1 => {
array2.forEach(arr2 => {
if (arr1.productId === arr2.productId &&
arr1.productName === arr2.productName ...
Object Array 1:
[
{
ProductId: 50,
ProductName: 'Test1',
Location: 77,
Supplier: 11,
Quantity: 33
},
{
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}
]
Object Array 2:
[
{
ProductId: 1,
ProductName: 'ABC',
Location: 3,
Supplier: 4,
Quantity: 52
},
{
ProductId: 2,
ProductName: 'DEF',
Location: 1,
Supplier: 2,
Quantity: 87
},
{
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
},
{
ProductId: 4,
ProductName: 'XYZ',
Location: 5,
Supplier: 6,
Quantity: 17
}
]
Resources:
How to determine if Javascript array contains an object with an attribute that equals a given value?
Javascript: Using `.includes` to find if an array of objects contains a specific object
Is there a way to tell if an object array has any common elements to another object array ? - Yes you can achieve this with the help of Array.some() method. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false.
const array1 = [{
ProductId: 50,
ProductName: 'Test1',
Location: 77,
Supplier: 11,
Quantity: 33
}, {
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}];
const array2 = [{
ProductId: 1,
ProductName: 'ABC',
Location: 3,
Supplier: 4,
Quantity: 52
}, {
ProductId: 2,
ProductName: 'DEF',
Location: 1,
Supplier: 2,
Quantity: 87
}, {
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}, {
ProductId: 4,
ProductName: 'XYZ',
Location: 5,
Supplier: 6,
Quantity: 17
}];
const isCommonProducts = array2.some(({ ProductId }) => array1.map(obj => obj.ProductId).includes(ProductId));
console.log(isCommonProducts);
Update : As per the author comment, we have to check all the properties of an object. Hence, we can achieve that by comparing the JSON string by converting the object into a string.
Live Demo :
const array1 = [{
ProductId: 50,
ProductName: 'Test1',
Location: 77,
Supplier: 11,
Quantity: 33
}, {
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}];
const array2 = [{
ProductId: 1,
ProductName: 'ABC',
Location: 3,
Supplier: 4,
Quantity: 52
}, {
ProductId: 2,
ProductName: 'DEF',
Location: 1,
Supplier: 2,
Quantity: 87
}, {
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}, {
ProductId: 4,
ProductName: 'XYZ',
Location: 5,
Supplier: 6,
Quantity: 17
}];
const getFilteredProducts = array2.filter(productObj => JSON.stringify(array1).indexOf(JSON.stringify(productObj)) !== -1);
console.log(getFilteredProducts);
If we can assume that each array's elements (we will call them sub-dictionaries) contain exactly the same keys in the same order, then this is my idea:
Convert each array into a new array whose elements are the JSON representations of the original sub-dictionaries values. This is an o(N) operation performed twice.
Of the new, converted arrays find the shortest one. Convert the other into a set. This is also o(N).
For each element of the shorter converted array, check to see if the set contains this value. This is also o(N).
let arr1 = [
{
ProductId: 50,
ProductName: 'Test1',
Location: 77,
Supplier: 11,
Quantity: 33
},
{
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
}
];
let arr2 = [
{
ProductId: 1,
ProductName: 'ABC',
Location: 3,
Supplier: 4,
Quantity: 52
},
{
ProductId: 2,
ProductName: 'DEF',
Location: 1,
Supplier: 2,
Quantity: 87
},
{
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
},
{
ProductId: 4,
ProductName: 'XYZ',
Location: 5,
Supplier: 6,
Quantity: 17
}
];
// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(arr));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(arr));});
// Find shortest array of JSON strings:
const l1 = arr1New.length;
const l2 = arr2New.length;
// enumerate shortest list
let list, set, l, arr;
if (l1 <= l2) {
list = arr1New;
set = new Set(arr2New);
l = l1;
arr = arr1;
}
else {
list = arr2New;
set = new Set(arr1New);
l = l2;
arr = arr2;
}
for(let i = 0; i < l; i++) {
if (set.has(list[i])) {
console.log(arr[i]);
}
}
Update
If the sub-dictionary keys are not necessarily in order, then we have to create new sub-dictionaries from these where the keys are in order:
// Create function to create new dictionaries sorted by keys
function sort_dict(d) {
items = Object.keys(d).map(function(key) {
return [key, d[key]];
});
items.sort(function(first, second) {
return first[0] < second[0] ? -1 : (first[0] > second[0] ? 1 : 0);
});
sorted_dict = {};
items.forEach(function(x) {
sorted_dict[x[0]] = x[1];
});
return(sorted_dict);
}
// And then we have these modified lines:
// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
Modified Code
let arr1 = [
{
ProductId: 50,
ProductName: 'Test1',
Location: 77,
Supplier: 11,
Quantity: 33
},
{
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25,
ProductId: 3 // Not in the same order as the others
}
];
let arr2 = [
{
ProductId: 1,
ProductName: 'ABC',
Location: 3,
Supplier: 4,
Quantity: 52
},
{
ProductId: 2,
ProductName: 'DEF',
Location: 1,
Supplier: 2,
Quantity: 87
},
{
ProductId: 3,
ProductName: 'GHI',
Location: 1,
Supplier: 4,
Quantity: 25
},
{
ProductId: 4,
ProductName: 'XYZ',
Location: 5,
Supplier: 6,
Quantity: 17
}
];
function sort_dict(d) {
items = Object.keys(d).map(function(key) {
return [key, d[key]];
});
items.sort(function(first, second) {
return first[0] < second[0] ? -1 : (first[0] > second[0] ? 1 : 0);
});
sorted_dict = {};
items.forEach(function(x) {
sorted_dict[x[0]] = x[1];
});
return(sorted_dict);
}
// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
// Find shortest array of JSON strings:
const l1 = arr1New.length;
const l2 = arr2New.length;
// enumerate shortest list
let list, set, l, arr;
if (l1 <= l2) {
list = arr1New;
set = new Set(arr2New);
l = l1;
arr = arr1;
}
else {
list = arr2New;
set = new Set(arr1New);
l = l2;
arr = arr2;
}
for(let i = 0; i < l; i++) {
if (set.has(list[i])) {
console.log(arr[i]);
}
}
For a simple yet reasonably fast solution, you can (1) use a Set of productIds from the first array, then (2) filter the second array based on the ids from the first one, this you only have to go over each array once O(n).
let arr1 = [
{
ProductId: 50,
ProductName: "Test1",
Location: 77,
Supplier: 11,
Quantity: 33,
},
{
ProductId: 3,
ProductName: "GHI",
Location: 1,
Supplier: 4,
Quantity: 25,
},
];
let arr2 = [
{
ProductId: 1,
ProductName: "ABC",
Location: 3,
Supplier: 4,
Quantity: 52,
},
{
ProductId: 2,
ProductName: "DEF",
Location: 1,
Supplier: 2,
Quantity: 87,
},
{
ProductId: 3,
ProductName: "GHI",
Location: 1,
Supplier: 4,
Quantity: 25,
},
{
ProductId: 4,
ProductName: "XYZ",
Location: 5,
Supplier: 6,
Quantity: 17,
},
];
const getCommonItems = (arr1, arr2) => {
let firstIdSet = new Set(arr1.map((product) => product.ProductId)); //1
return arr2.filter((product) => firstIdSet.has(product.ProductId)); //2
};
console.log(getCommonItems(arr1, arr2));
If you want a deep equality comparison(for nested objects or for all (key, value) pairs), I would suggest a slightly better approach which is using the base64 encoding/decoding to improve on comparison performance.
So my approach is to:
merge the arrays and convert the object to base64 strings.
Group the recurrences together
Filter on duplicates
revert the base64 strings into their original object.
const convertObjToBase64 = o => btoa(JSON.stringify(o));
const convertBase64ToObj = str => JSON.parse(atob(str));
const arrayToObjCount = arr => arr.reduce((res, v) => {
res[v] = (res[v] ?? 0) + 1;
return res;
}, {});
const findDuplicateObjectsInMultipleArrays = (...arrays) => {
const base64Array = Array.from(arrays.flat(), convertObjToBase64);
const objCount = arrayToObjCount(base64Array);
const duplicates = Object.entries(objCount).reduce((prev, [k, v]) => {
if (v > 1) {
prev.push(convertBase64ToObj(k));
}
return prev;
}, []);
return duplicates;
}
let arr1 = [{
ProductId: 50,
ProductName: 'Test1',
Location: {
LocationId: 77,
LocationName: 'Location 77'
},
Supplier: 11,
Quantity: 33
},
{
ProductId: 3,
ProductName: 'GHI',
Location: {
LocationId: 1,
LocationName: 'Location 1'
},
Supplier: 4,
Quantity: 25
}
];
let arr2 = [{
ProductId: 1,
ProductName: 'ABC',
Location: {
LocationId: 3,
LocationName: 'Location 3'
},
Supplier: 4,
Quantity: 52
},
{
ProductId: 2,
ProductName: 'DEF',
Location: {
LocationId: 1,
LocationName: 'Location 1'
},
Supplier: 2,
Quantity: 87
},
{
ProductId: 3,
ProductName: 'GHI',
Location: {
LocationId: 1,
LocationName: 'Location 1'
},
Supplier: 4,
Quantity: 25
},
{
ProductId: 4,
ProductName: 'XYZ',
Location: {
LocationId: 5,
LocationName: 'Location 5'
},
Supplier: 6,
Quantity: 17
}
];
let arr3 =[
{
ProductId: 2,
ProductName: 'DEF',
Location: {
LocationId: 1,
LocationName: 'Location 1'
},
Supplier: 2,
Quantity: 87
},
{
ProductId: 3,
ProductName: 'GHI',
Location: {
LocationId: 2,
LocationName: 'Location 2'
},
Supplier: 4,
Quantity: 25
},
{
ProductId: 4,
ProductName: 'XYZ',
Location: {
LocationId: 6,
LocationName: 'Location 5'
},
Supplier: 6,
Quantity: 17
}
];
console.log(findDuplicateObjectsInMultipleArrays(arr1, arr2, arr3));
I will post two solutions:
First Solution is readable one
Code is not 100% performance optimized, but it is readable and elegant.
Playground link with working code
First, we need a method that compares two objects of any type. The method compares the first-level properties, so if we have nested object properties, it will compare them by reference.
const areTheSame = (a: any, b: any) => {
const objAProps = Object.keys(a).filter(key => typeof a[key] !== "function")
const objBProps = Object.keys(b).filter(key => typeof b[key] !== "function")
if (objAProps.length !== objBProps.length) {
return false;
}
return objAProps.every((propName) => a[propName] === b[propName]);
}
And then we can implement readable intersect method which will work for any array types:
const getIntersection = (array1: Array<any>, array2: Array<any>) => {
return array1.filter(a1Item => array2.some(a2Item => areTheSame(a1Item, a2Item)));
}
The Second solution is performance-oriented, its drawback is that it is not so readable
First, we calculate the has for all objects, then within a single forEach loop we can identify the intersection based on that Hash. I have used md5, but any hash algorithm or library can be used.
Hers is stack blitz link playground. It can be run, ignore the import error.
const getArrayIntersection = (
firstArray: Array<any>,
secondArray: Array<any>
) => {
const array1Hashed = firstArray.map((i) => md5(JSON.stringify(i)));
const array2Set = new Set(secondArray.map((i) => md5(JSON.stringify(i))));
const result: Array<any> = [];
array1Hashed.forEach((itemHash, index) => {
if (array2Set.has(itemHash)) {
result.push(firstArray[index]);
}
});
return result;
};
Just to piggyback #Rohìt Jíndal, you can check if an array has a specific object like so:
const resultObj = arr1.filter(obj => obj.id=== "whatever" && obj.productname == "whatever") // ETC ETC

Conditional copying properties and values from one array of objects to another

Got two arrays of objects and my goal is to check if the value under property id from array1 matches the value under the property categoryId of array2. When find a match want to add the missing property amount to the relevant member of array1 or create a new array containing all the properties and values I need - id, name, amount
Here are the two arrays:
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}]
and
const array2 = [
{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},]
Tried to combine different approaches from answers to similar but simpler cases already posted in the community but didn't managed to make them work in my case.
Loop through each item in array1, then loop through each item in array2 inside the loop and check whether the categoryId is equal to the id.
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}
]
const array2 = [{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},
]
array1.forEach((e) => {
array2.forEach((f) => {
if (f.categoryId == e.id) {
e.amount = f.expence.amount;
}
})
})
console.log(array1);
You can also make use of Array.filter to find the item where the categoryId is equal to the id:
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}
]
const array2 = [{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},
]
array1.forEach((e) => {
var arr = array2.filter(f => f.categoryId == e.id);
if(arr.length > 0) e.amount = arr[0].expence.amount;
})
console.log(array1);

How do I get the total sum of nested arrays in Reactjs?

I want to get the total price of nested arrays in a specific category e.g: Hot Drinks.
Here is a sample of what I have now, so I want to filter out and get the total price of Hot Drinks Category only.
[
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
]
You can apply a filter method on the array and then just add the values on the filtered array. Something like below:
let prod = [
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
];
function getPriceByCategory(category, products) {
let price = 0;
products.forEach(orders => {
orders.orderItems.filter(order => order.category == category).forEach(item => {
price += item.price;
});
});
return price;
}
const totalPrice = getPriceByCategory('Hot Drinks', prod);
alert(totalPrice);
Sample JS Fiddle: https://jsfiddle.net/sagarag05/qwzju53f/9/
const filterBy = 'Hot Drinks';
const items = [
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
]
const sumOf = (items, filterBy) => {
let totalPrice = 0;
items.forEach(item => {
item.orderItems.forEach(orderItem => {
if (orderItem.category === filterBy) {
totalPrice += orderItem.price;
}
})
})
return totalPrice;
}
console.log(sumOf(items, filterBy))
let sum = 0;
allOrders.forEach(order => {
order.orderItems.forEach(item => {
if(item.category=='Hot Drinks') {
sum+ = item.price * item.qty
}});
});
sum has the total price for Hot Drinks
Assuming you named that information as data:
Generate a big array of all the "orderItems"
For each of those elements sum the price if the category is "Hot Drinks"
const totalPrice = data
.reduce((acc, { orderItems }) => [...acc, ...orderItems], [])
.reduce((acc, { category, price }) => category === "Hot Drinks" ? acc + price : acc, 0);
console.log(totalPrice); // 10
Use flatMap and reduce or alternatively using forEach and destructuring
const total = (arr, text) =>
arr
.flatMap(({ orderItems }) => orderItems)
.reduce((acc, { category, price }) =>
(acc + (category === text ? price : 0)), 0);
// alternatively
const total2 = (arr, text, acc = 0) => {
arr.forEach(({ orderItems }) =>
orderItems.forEach(
({ category, price }) => (category === text && (acc += price))
)
);
return acc;
};
const data = [
{
totalPrice: 30,
_id: "6014fa4324e125599eaa72b5",
orderItems: [
{
_id: "6014fa4324e125599eaa747ss",
category: "Breakfast",
name: "food name 1",
price: 3,
qty: 1,
},
{
_id: "6014fa4324e125599eaa747s5",
category: "Hot Drinks",
name: "drink name 1",
price: 3,
qty: 5,
},
{
_id: "6014fa4324e125599eaa74767",
category: "Hot Drinks",
name: "drink name 2",
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: "6014fa4324e125599eaa7276e",
orderItems: [
{
_id: "6014fa4324e125599eaa747ss",
category: "Hot Drinks",
name: "drink name 1",
price: 3,
qty: 6,
},
],
},
];
console.log(total(data, 'Hot Drinks'))
console.log(total2(data, 'Hot Drinks'))

Find index of object array

I'm stuck with the code. I actually want to find the index of elements in dataLayer using typescript/javascript.
track: { products },
dataLayers: { current: { cart: { items } } }
products?: IProduct[]
export interface IProduct {
id: string
quantity?: string
name?: string
}
items?: ICartItem[]
export interface ICartItem {
id: string
brand: string
name: string
quantity: number
}
track: { products }
products have {id,quantity}
dataLayers: { current: { cart: { items } } }
items have {id, brand, name, quantity }
Now I want to find the position/index of products, For Example:
Example: *
products:{
[{id: 'a123',quantity: '1'},{id:'a345', quantity:'2'}]
}
items:{
[{id: 'a123',brand:'pen',name: 'Reynolds', quantity: '1'}, {id: 'a143',brand:'pencil',name: 'Nataraj', quantity: '3'}, {id: 'a122',brand:'pen',name: 'Parker',quantity: '1'},{id:'a345',brand:'Eraser',name: 'Faber-Castell', quantity:'2'}]
}
position of {id: 'a123',quantity: '1'} should be 1 and position of {id:'a345', quantity:'2'} should be 4
My code looks like below:
I have used map to get id from products as shown
const id = products.map(product => { return product.id})
now I want to find index/position of those products in items.so I tried like below
let position = filter((item: ICartItem) => {
if(id.includes(item.id)){
return {id}
}
}).findindex(id)
i.e.,
const id = products.map(product => { return product.id})
let position = filter((item: ICartItem) => {
if(id.includes(item.id)){
return {id}
}
}).findindex(id)
but I getting error
Argument of type 'string[]' is not assignable to parameter of type '(value: ICartItem, index: number, obj: ICartItem[]) => unknown'.
Type 'string[]' provides no match for the signature '(value: ICartItem, index: number, obj: ICartItem[]): unknown'.ts(2345)
an Some help me in finding position/index of the filtered product?
After map you can chain .indexOf() to get the Index of the object and Index of a345 would be 3 not 4 because indexes are zero based in an array
let items = [{
id: 'a123',
brand: 'pen',
name: 'Reynolds',
quantity: '1'
}, {
id: 'a143',
brand: 'pencil',
name: 'Nataraj',
quantity: '3'
}, {
id: 'a122',
brand: 'pen',
name: 'Parker',
quantity: '1'
}, {
id: 'a345',
brand: 'Eraser',
name: 'Faber-Castell',
quantity: '2'
}]
let index= items.map(function(e) {
return e.id;
}).indexOf('a345')
console.log(`Index=${index}`)
console.log(`position= ${index+1}`)
You can chain map and indexOf to get result as an array with positions.
const products = [
{ id: 'a123', quantity: '1' },
{ id: 'a345', quantity: '2' },
];
const items = [
{ id: 'a123', brand: 'pen', name: 'Reynolds', quantity: '1' },
{ id: 'a143', brand: 'pencil', name: 'Nataraj', quantity: '3' },
{ id: 'a122', brand: 'pen', name: 'Parker', quantity: '1' },
{ id: 'a345', brand: 'Eraser', name: 'Faber-Castell', quantity: '2' },
];
const result = products.map(product => items.map(item => item.id).indexOf(product.id) + 1);
console.log(result)
If array with positions needs to return id as well then you can do:
const products = [
{ id: 'a123', quantity: '1' },
{ id: 'a345', quantity: '2' },
];
const items = [
{ id: 'a123', brand: 'pen', name: 'Reynolds', quantity: '1' },
{ id: 'a143', brand: 'pencil', name: 'Nataraj', quantity: '3' },
{ id: 'a122', brand: 'pen', name: 'Parker', quantity: '1' },
{ id: 'a345', brand: 'Eraser', name: 'Faber-Castell', quantity: '2' },
];
const result = products.map(product => {
const id = product.id;
return {
id,
position: items.map(item => item.id).indexOf(id) + 1
}
});
console.log(result)
You can try:
const ids = products.map( product => product.id);
const positions = items.map( ( item, i ) => {
if( ids.includes(item.id)){
return{
position: i + 1,
id: item.id
}
}
return null
}).filter(Boolean)
//[ { position: 1, id: 'a123' }, { position: 4, id: 'a345' } ]

Lodash Merge Two Arrays and categorize it

i neeed to merge two arrays: Categories and Products. Each product has a category object. I need to organize by category, include the category object and keep the empty categories. GroupBy function include only one parameter.
const Categories= [
{id: 1, 'name': 'category1'}
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'},
]
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
]
expected result
const result = [
{
category: {id: 1, name: 'category1'},
products:[{id:1, name: 'produt1'}, {id: 2, name: 'produto1'} ]
},
{
category: {id: 2, name: 'category2'},
products:[{id:3, name: 'produt3'}, {id: 4, name: 'produto4'} ]
},
{
category: {id: 3, name: 'category3'},
products:[]
},
{
category: {id: 4, name: 'category4'},
products:[]
},
]
attempts:
for (i = 0; i < categoriesJson.length; i++) {
categoriesJson[i] = _.assign({}, categoriesJson[i], { products: [] })
for (j = 0; j < productsJson.length; j++) {
if(productsJson[j].categoryId.objectId === categoriesJson[i].objectId){
categoriesJson[i].products.push(productsJson[j])
}
}
}
Concat the Categories (formatted by to a Product format) to the Products, group by the category.id, and then map each group - category is taken from the 1st item, while products are the the items in groups, without the category, and empty items are rejected:
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = _(Products)
.concat(Categories.map(category => ({ category })))
.groupBy('category.id')
.map(group => ({
category: _.head(group).category,
products: _(group)
.map(o => _.omit(o, 'category'))
.reject(_.isEmpty)
.value()
}))
.value()
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
And the same idea with lodash/fp. Wrap the _.flow() with the _.useWith() function, and preformat the Categories (2nd param) to fit the Categories. The rest is similar to the lodash chain.
const { useWith, identity, flow, concat, groupBy, map, head, omit, reject, isEmpty } = _
const formatProducts = flow(map(omit('category')), reject(isEmpty))
const fn = useWith(flow(
concat,
groupBy('category.id'),
map(group => ({
category: head(group).category,
products: formatProducts(group)
}))
), [identity, map(category => ({ category }))])
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = fn(Products, Categories)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
If lodash is not a requirement in the solution, this is how I did it with plain javascript;
const Categories= [
{id: 1, 'name': 'category1'},
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'}
];
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
];
const result = [];
for (let index in Categories) {
let category_id = Categories[index].id;
result.push({
category: Categories[index],
products: GetProductsWithCategoryId(category_id)
});
}
function GetProductsWithCategoryId(category_id) {
let products = [];
for (let index in Products) {
if (Products[index].category.id == category_id) {
products.push({
id: Products[index].id,
name: Products[index].name
});
}
}
return products;
}
console.log("result:", result);
Using reduce, create a mappedProducts object which groups the Products based on the category.id. Like this:
{
"1": [{ id: 1, name: "product1" }, { id: 2, name: "product2" }],
"2": [{ id: 3, name: "product3" }, { id: 4, name: "product4" }]
}
Then, map the Categories array and get the output for each category
const Categories=[{id:1,name:"category1"},{id:2,name:"category2"},{id:3,name:"category3"},{id:4,name:"category4"},],
Products=[{id:1,name:"product1",category:{id:1,name:"category1"}},{id:2,name:"product2",category:{id:1,name:"category1"}},{id:3,name:"product3",category:{id:2,name:"category2"}},{id:4,name:"product4",category:{id:2,name:"category2"}}];
const mappedProducts = Products.reduce((acc, { category, ...rest }) => {
acc[category.id] = acc[category.id] || [];
acc[category.id].push(rest)
return acc;
}, {})
const output = Categories.map(category => ({
category,
products: mappedProducts[category.id] || []
}))
console.log(output)
In a single function. Lodash is not necessary:
const Categories = [
{ id: 1, name: "category1" },
{ id: 2, name: "category2" },
{ id: 3, name: "category3" },
{ id: 4, name: "category4" }
];
const Products = [
{ id: 1, name: "product1", category: { id: 1, name: "category1" } },
{ id: 2, name: "product2", category: { id: 1, name: "category1" } },
{ id: 3, name: "product3", category: { id: 2, name: "category2" } },
{ id: 4, name: "product4", category: { id: 2, name: "category2" } }
];
function combine(categories, products) {
return categories.reduce((list, category) => {
const nextItem = {
category,
products: [
products.filter(p => p.category.id === category.id).map(
({ id, name }) => ({
id,
name
})
)
]
};
list.push(nextItem);
return list;
}, []);
}
const result = combine(Categories, Products)
Now for your information, if you had a huge list of categories and/or products, this wouldn't be the ideal solution as there is a lot of looping involved. Instead, you would cache products in such a way that you only ever need to look at a given product once (rather than looking at every product for every category). With a small data set, this optimization isn't necessary.

Categories