Count Unique Value from Two object of array in Javascript - javascript

Given an array of categories and an array of entries, creates an array of objects with a category name and an entry count. Consider id is equal to categoryId.
var categories = [
{ name: 'Cats', id: 10 },
{ name: 'Dogs', id: 20 },
];
var entries = [
{categoryId: 10, name: 'Fluffy'},
{categoryId: 10, name: 'Spot'},
{categoryId: 10, name: 'Lil'},
{categoryId: 20, name: 'Tom'},
{categoryId: 20, name: 'Buck'},
{categoryId: 20, name: 'Flo'},
{categoryId: 20, name: 'Cheek'},
{categoryId: 10, name: 'Stan'},
{categoryId: 20, name: 'Stila'}
]
Expected Output: [{ name:'Cats', count: 4 }, { name:'Dogs', count: 5 }];
I wrote it like this below, but there seem to be a performance problem when you try to run it through hundreds of categories and tens of thousands of entries.
const categoriesByEntryCount = (categories, entries) =>
categories.map(category => ({
name: category.name,
count: entries.filter(entry => entry.categoryId === category.id).length,
}));
My question is there another way to write or implement this ?

You need to use Maps in all possible places.
var categories = new Map();
categories.set(10, 'Cats');
categories.set(20, 'Dogs');
var entries = [
{ categoryId: 10, name: 'Fluffy' },
{ categoryId: 10, name: 'Spot' },
{ categoryId: 10, name: 'Lil' },
{ categoryId: 20, name: 'Tom' },
{ categoryId: 20, name: 'Buck' },
{ categoryId: 20, name: 'Flo' },
{ categoryId: 20, name: 'Cheek' },
{ categoryId: 10, name: 'Stan' },
{ categoryId: 20, name: 'Stila' },
];
console.log(Array.from(
entries.reduce(
(m, { categoryId, name }) =>
m.set(categoryId, (m.get(categoryId) || 1) + 1),
new Map()
),
([k, v]) => ({ name: categories.get(k), count: v })
));

const categories = [ { name: 'Cats', id: 10 }, { name: 'Dogs', id: 20 } ];
const entries = [ { categoryId: 10, name: 'Fluffy' }, { categoryId: 10, name: 'Spot' }, { categoryId: 10, name: 'Lil' }, { categoryId: 20, name: 'Tom' }, { categoryId: 20, name: 'Buck' }, { categoryId: 20, name: 'Flo' }, { categoryId: 20, name: 'Cheek' }, { categoryId: 10, name: 'Stan' }, { categoryId: 20, name: 'Stila' } ];
// get number of occurences of each category in entries
const categoriesCount = entries.reduce((countMap, { categoryId }) =>
countMap.set( categoryId, 1 + (countMap.get(categoryId) || 0) )
, new Map);
// iterate over categories and return name and count in categoriesCount
const res = categories.map(({ name, id }) =>
({ name, count: categoriesCount.get(id) })
);
console.log(res);

We can do like below with time complexity O(M + N)
var categories = [
{ name: 'Cats', id: 10 },
{ name: 'Dogs', id: 20 },
];
var entries = [
{categoryId: 10, name: 'Fluffy'},
{categoryId: 10, name: 'Spot'},
{categoryId: 10, name: 'Lil'},
{categoryId: 20, name: 'Tom'},
{categoryId: 20, name: 'Buck'},
{categoryId: 20, name: 'Flo'},
{categoryId: 20, name: 'Cheek'},
{categoryId: 10, name: 'Stan'},
{categoryId: 20, name: 'Stila'}
]
const categoriesByEntryCount = (categories, entries) => {
const entriesHash = entries.reduce((acc, ele) => {
acc[ele.categoryId] = acc[ele.categoryId] ? acc[ele.categoryId] + 1 : 1;
return acc;
}, {});
return categories.map(category => ({
name: category.name,
count: entriesHash[category.id],
}));
}
console.log(categoriesByEntryCount(categories, entries))

This is obviously a reducing job.
var categories = [ { name: 'Cats', id: 10 }
, { name: 'Dogs', id: 20 }
],
entries = [ {categoryId: 10, name: 'Fluffy'}
, {categoryId: 10, name: 'Spot'}
, {categoryId: 10, name: 'Lil'}
, {categoryId: 20, name: 'Tom'}
, {categoryId: 20, name: 'Buck'}
, {categoryId: 20, name: 'Flo'}
, {categoryId: 20, name: 'Cheek'}
, {categoryId: 10, name: 'Stan'}
, {categoryId: 20, name: 'Stila'}
],
result = entries.reduce((cs,e) => ( cs.map(c => c.id === e.categoryId ? c.count ? c.count++
: c.count = 1
: c)
, cs
), categories);
console.log(result);
You may complain that the result includes the id property but that's just good.

Related

comparing objects in useState and if there is object with same id keep one of them

i'm react beginner, for some reason when i console log i get two japans any advices ?
these are my data:
options initial value (comes from props) is:
[{ id: 1, name: 'Japan' },{ id: 4, name: 'Australia' }, { id: 5, name: 'Poland' }];
and from redux i'm getting this:
[{ id: 1, name: 'Japan' }, { id: 2, name: 'America' }, { id: 3, name: 'Sweden' }];
but my expected out put is :
[{ id: 1, name: 'Japan' },{ id: 4, name: 'Australia' }, { id: 5, name: 'Poland' }, { id: 2, name: 'America' }, { id: 3, name: 'Sweden' }]
const getUnselectedValues = useSelector(UnselectedValues);
const [options, setOptions] = useState(
props.selectedValues
? (
[...props.selectedValues, ...getUnselectedValues]
).filter((e ) => e)
: [...getUnselectedValues]
);
console.log('options:', options)
Try it:
const [options, setOptions] = useState(
props.selectedValues
? Object.values([...props.selectedValues, ...getUnselectedValues].reduce((acc, {id, name}) =>(acc[id] ??= {id, name}, acc),{}))
: [...getUnselectedValues]
);
Approach:
Iterate through the merged array and build a dictionary by using reduce() where the key is the id and the value is {id, value}
In every iteration look up in the dictionary whether has the key in it or not. If the key is not present that means it's a unique entry and inserts it. If the key is already in the dictionary that means the entry is not unique so no need to insert it again.
Here is an example in Vanilla JS so you can play around:
const selectedValues = [{ id: 1, name: 'Japan' },{ id: 4, name: 'Australia' }, { id: 5, name: 'Poland' }];
const getUnselectedValues = [{ id: 1, name: 'Japan' }, { id: 2, name: 'America' }, { id: 3, name: 'Sweden' }];
const res = Object.values([...selectedValues, ...getUnselectedValues].reduce((acc, {id, name}) =>(acc[id] ??= {id, name}, acc),{}));
console.log(res);
Using filter():
const selectedValues = [{ id: 1, name: 'Japan' },{ id: 4, name: 'Australia' }, { id: 5, name: 'Poland' }];
const getUnselectedValues = [{ id: 1, name: 'Japan' }, { id: 2, name: 'America' }, { id: 3, name: 'Sweden' }];
const res = [...selectedValues, ...getUnselectedValues.filter(({id, name}) => !selectedValues.find(it => it.id === id))];
console.log(res);

Getting an array from array of array objects

I have an array which consists of an array objects as shown:
dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
I need to extract an array consisting of only arrObj objects:
var newArr = [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
},
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
];
Tried using reduce method unsuccessfully:
dataArr.reduce((previousValue, currentValue, currentIndex, array) => {
return previousValue. arrObj.concat(currentValue.arrObj)
});
Let me know how to do this. Thanks
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let result = dataArr.flatMap(e => e.arrObj)
console.log(result)
You were pretty close.
There's no arrObj property in your result, it's just an array.
You need to provide an empty array as the initial value argument to reduce().
const dataArr = [{
id: 1,
arrObj: [{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
const newArr = dataArr.reduce((previousValue, currentValue) => {
return previousValue.concat(currentValue.arrObj)
}, []);
console.log(newArr);
You could use in one line by using Spread Operator...
dataArr.reduce((previousValue, currentValue) => [...previousValue?.arrObj, ...currentValue?.arrObj]);
Tip: use Optional chaining ?. in case there is no property arrObj!
You can use the Array#reduce method as follows:
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let newArr = dataArr.reduce((acc,cur) => [...acc, ...cur.arrObj], []);
console.log( newArr );

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'))

Duplicate objects in array of objects by count value

I am looking for a way to modify array of objects like this:
[
{
id: 1,
name: 'xyz',
count: 3,
},
{
id: 2,
name: 'aaa',
count: 2,
},
{
id: 6,
name: 'bbb',
count: 1,
},
]
Now I want to map it shomehow to receive new array of objects without count properties but with duplicated objects by its count value. We will have:
[
{
id: 1,
name: 'xyz',
},
{
id: 1,
name: 'xyz',
},
{
id: 1,
name: 'xyz',
},
{
id: 2,
name: 'aaa',
},
{
id: 2,
name: 'aaa',
},
{
id: 6,
name: 'bbb',
},
]
I tried to do it with map and reduce but it didn't work out as expected...
You could use a nested mapping with an outer Array#flatMap.
var data = [{ id: 1, name: 'xyz', count: 3 }, { id: 2, name: 'aaa', count: 2 }, { id: 6, name: 'bbb', count: 1 }],
result = data.flatMap(({ count, ...o }) =>
Array.from({ length: count }, _ => ({ ... o })));
console.log(result);
Nina Scholz solution works fine, if you want something easier to read:
var data = [{
id: 1,
name: 'xyz',
count: 3,
},
{
id: 2,
name: 'aaa',
count: 2,
},
{
id: 6,
name: 'bbb',
count: 1,
},
];
var output = [];
for (var i = 0; i < data.length; i++) {
var element = data[i];
for (var j = 0; j < element.count; j++) {
output.push({
id: element.id,
name: element.name
});
}
}
console.log(output);

Filter and combine fields from 2 arrays with ES6 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);

Categories