How to get objects with min values by category - javascript

I believe I have an array of javascript objects as follows, and I am trying to filter out all but the lowest prices for each item:
Have:
const fruits = [
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 2, fruit: "apple", store: "store2", price: 1.25},
{id: 3, fruit: "banana", store: "store1", price: 0.5},
{id: 4, fruit: "banana", store: "store2", price: 0.75},
{id: 5, fruit: "banana", store: "store3", price: 0.75}
];
Want:
[
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 3, fruit: "banana", store: "store1", price: 0.5}
];
I imagine in SQL this would be accomplished with a group by statement such as "select fruit, min(price) from table group by fruit", but am not sure how I might accomplish this in its current object form.
I am looking for a JavaScript solution but am open to changing the data structure or using a framework if needed. I am already using React.js if that is relevant. Any guidance?

object assign and reduce:
const fruits =
[ { id: 1, fruit: "apple", store: "store1", price: 1 }
, { id: 2, fruit: "apple", store: "store2", price: 1.25 }
, { id: 3, fruit: "banana", store: "store1", price: 0.5 }
, { id: 4, fruit: "banana", store: "store2", price: 0.75 }
, { id: 5, fruit: "banana", store: "store3", price: 0.75 }
]
const mini = fruits.reduce((a,c)=>
{
let x = a.find(e=>e.fruit===c.fruit)
if (!x) a.push(Object.assign({},c))
else if (x.price > c.price) Object.assign(x,c)
return a
},[])
console.log( mini )
– Dave Newton : . With a map/object-based solution you iterate fruits once and let the result object handle lookups.
there is an other way for this :
const fruits =
[ { id: 1, fruit: "apple", store: "store1", price: 1 }
, { id: 2, fruit: "apple", store: "store2", price: 1.25 }
, { id: 3, fruit: "banana", store: "store1", price: 0.5 }
, { id: 4, fruit: "banana", store: "store2", price: 0.75 }
, { id: 5, fruit: "banana", store: "store3", price: 0.75 }
]
const mini = fruits.reduce((a,c,i,t)=>
{
if (!a.find(e=>e.fruit===c.fruit))
a.push(t.filter(e=>e.fruit===c.fruit).reduce((r,n)=>r.price<n.price?r:n) )
return a
},[])
console.log( mini )

You can do this via a reduce operation, creating a map of category (fruit) to lowest price records.
For example
const fruits = [
{id: 1, fruit: "apple", store: "store1", price: 1},
{id: 2, fruit: "apple", store: "store2", price: 1.25},
{id: 3, fruit: "banana", store: "store1", price: 0.5},
{id: 4, fruit: "banana", store: "store2", price: 0.75},
{id: 5, fruit: "banana", store: "store3", price: 0.75}
]
const result = [...fruits.reduce((map, entry) => {
// check for an existing record and if it has a greater price
if (!map.has(entry.fruit) || map.get(entry.fruit).price > entry.price) {
// set the new record for this category
map.set(entry.fruit, entry)
}
return map
}, new Map()).values()] // now just get the values
console.info(result)
If there are multiple entries with the same price, this will keep the first one found. If you want to keep the last, use a >= comparison instead.

groupBy is a good commonly needed operation to have handy. I'd use that first to group the objects by fruit. The second reduce (minInArray) also is a commonly used form to find min or max in an array. Together they produce the min for each fruit type...
const groupBy = (array, key) => {
return array.reduce(function(r, a) {
r[a[key]] = r[a[key]] || []
r[a[key]].push(a)
return r
}, {})
}
const minInArray = (array, key) => {
return array.reduce((prev, curr) => {
return prev[key] < curr[key] ? prev : curr
})
}
const fruits = [{
id: 1,
fruit: "apple",
store: "store1",
price: 1
}, {
id: 2,
fruit: "apple",
store: "store2",
price: 1.25
}, {
id: 3,
fruit: "banana",
store: "store1",
price: 0.5
}, {
id: 4,
fruit: "banana",
store: "store2",
price: 0.75
}, {
id: 5,
fruit: "banana",
store: "store3",
price: 0.75
}];
let groups = groupBy(fruits, 'fruit')
let minPrices = Object.keys(groups).map(fruit => {
return minInArray(groups[fruit], 'price')
})
console.log(minPrices)

Related

Get quantity in object respective with the category

I want the result to sum all the quantity of same cat.
var data = [
{ cat: 'EK-1',name:"test",info:"mat", quantity: 3},
{ cat: 'EK-2', name:"test2",info:"nat"quantity: 1}
];
I have array of object having some similar objects. How to add quantity and create unique object? This is 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,
quantitya
}));
console.log(products);
You first group and sum quantities under categories keys, using reduce and then you discard those keys with Object.values.
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 result = Object.values(data.reduce((acc, item) => {
if (!acc[item.cat]) {
acc[item.cat] = item;
} else {
acc[item.cat] = { ...item, quantity: item.quantity + acc[item.cat].quantity }
}
return acc;
}, {}))
console.log(result)
You could get the complete object as value for the map and increment quantity.
const
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 }],
products = Array.from(
data
.reduce(
(acc, o) => acc.set(o.cat, { ...o, quantity: (acc.get(o.cat)?.quantity || 0) + o.quantity }),
new Map
)
.values()
);
console.log(products);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to display all objects that contain all item from array - javascript

Need for some help here. Looking for a hint to solve this issue :
The goal is to filter arrayOfObject and get all objects with the property fruits containing all the element from the given array.
const arrayOfObject = [
{
id: 1,
country: 'USA',
fruits: ["APPLE", "ORANGE", "BANANA"]
},
{
id: 2,
country: 'Canada',
fruits: ["APPLE"]
},
{
id: 3,
country: 'France',
fruits: ["ORANGE", "BANANA", "LEMON"]
},
{
id: 4,
country: 'Mexico',
fruits: ["BANANA", "PYTHON", "CHERRY"]
},
{
id: 5,
country: 'Ukraine',
fruits: ["APPLE", "ORANGE", "CHERRY"]
},
{
id: 6,
country: 'Italy',
fruits: ["APPLE", "ORANGE", "BANANA", "LEMON", "CHERRY"]
}
];
First exemple with this given array :
const firstArrayOfFruits = ["APPLE","ORANGE","BANANA"];
Should render =>
[
{
id: 1,
country: 'USA',
fruits: ["APPLE", "ORANGE", "BANANA"]
},
{
id: 6,
country: 'Italy',
fruits: ["APPLE", "ORANGE", "BANANA", "LEMON", "CHERRY"]
}
]
Second exemple with this given array :
const secondArrayOfFruits = ["APPLE","ORANGE"];
Should render =>
[
{
id: 1,
country: 'USA',
fruits: ["APPLE", "ORANGE", "BANANA"]
},
{
id: 5,
country: 'Ukraine',
fruits: ["APPLE", "ORANGE", "CHERRY"]
},
{
id: 6,
country: 'Italy',
fruits: ["APPLE", "ORANGE", "BANANA", "LEMON", "CHERRY"]
}
]
You can use Array.prototype.filter:
const arrayOfObject = [{
id: 1,
country: 'USA',
fruits: ["APPLE", "ORANGE", "BANANA"]
},
{
id: 2,
country: 'Canada',
fruits: ["APPLE"]
},
{
id: 3,
country: 'France',
fruits: ["ORANGE", "BANANA", "LEMON"]
},
{
id: 4,
country: 'Mexico',
fruits: ["BANANA", "PYTHON", "CHERRY"]
},
{
id: 5,
country: 'Ukraine',
fruits: ["APPLE", "ORANGE", "CHERRY"]
},
{
id: 6,
country: 'Italy',
fruits: ["APPLE", "ORANGE", "BANANA", "LEMON", "CHERRY"]
}
];
const firstArrayOfFruits = ["APPLE", "ORANGE", "BANANA"];
var arr = arrayOfObject.filter(item => item.fruits.filter(fruit => firstArrayOfFruits.indexOf(fruit) + 1).length >= firstArrayOfFruits.length);
console.log(arr);
Using ES6 some() function is useful because when finds an entry it will return true and doesnt check the rest, so you can save some calcs, time and CPU
const filtered = arrayOfObject.filter(obj => {
if(obj.fruits.some(fruit => fruitsLisToCheck.includes(fruit)))
return obj
})
const fruitsLisToCheck = ['BANANA', 'APPLE'];
const arrayOfObject = [
{
id: 2,
country: 'Canada',
fruits: ["APPLE"]
},
{
id: 3,
country: 'France',
fruits: ["BANANA"]
},
{
id: 4,
country: 'Mexico',
fruits: ["CHERRY"]
},
];

pushing a string into array return a number and not the actual string?

I am trying pushing the name but in the console log I see just a number and not the actual name
What am i doing wrong??
const cartItems = [{
id: 1,
name: "Soup",
price: 3,
category: "starters",
count: 1
},
{
id: 2,
name: "Pâté",
price: 5,
category: "starters",
count: 1
},
{
id: 9,
name: "Sticky toffee",
price: 18,
category: "desserts",
count: 1
}
]
var dishesArray = [];
var groupByCategory = []
cartItems.reduce(function(res, value) {
if (!res[value.category]) {
res[value.category] = {
category: value.category,
count: 0,
dishes: dishesArray.push(value.name), // problem here
};
groupByCategory.push(res[value.category]);
}
res[value.category].count += value.count;
return res;
}, {});
console.log(groupByCategory)
Expected output
[{category: "starters", count: 2, dishes:["Soup","Pâté"]},
{category: "desserts", count: 1, dishes:["Sticky toffee"]}]
As mentioned, a push returns an int
I believe you are looking for this much shortened reduce
const cartItems = [
{ id: 1, name: "Soup", price: 3, category: "starters", count: 1 },
{ id: 2, name: "Pâté", price: 5, category: "starters", count: 1 },
{ id: 9, name: "Sticky toffee", price: 18, category: "desserts", count: 1}
];
const groupByCategory = cartItems.reduce(function(res, value) {
const cat = value.category;
res[cat] = res[cat] || { category: cat, count: 0, dishes: [] };
res[cat].count += value.count;
res[cat].dishes.push(value.name)
return res;
}, {});
console.log(groupByCategory)

Sort aray of objects by another array of objects reference

I need some help to sort this data out, i have an array of products and i need to sort and display by settings configuration. The output must have the same order as settings array (index) and if display is true. Thanks in advance. This is what i tryed:
var products = [
{id: 0, name: 'Chocolate', category: 'Sweet'},
{id: 1, name: 'Almendras', category: 'Fruit'},
{id: 2, name: 'Nueces', category: 'Fruit'},
{id: 3, name: 'Mermelada', category: 'Jam'},
{id: 4, name: 'Alfajor', category: 'Sweet'},
{id: 5, name: 'Queso', category: 'UwU'},
{id: 6, name: 'Arandanos', category: 'Fruit'},
{id: 7, name: 'Maracuya', category: 'Fruit'}
];
let settings = [
{
name: 'Fruit',
display: true
},
{
name: 'Jam',
display: false
},
{
name: 'Sweet',
display: true
},
{
name: 'UwU',
display: true
}
]
let group = products.reduce((r, a) => {
r[a.category] = [...r[a.category] || [], a];
return r;
}, {});
let arrangedProducts = Object.keys(group);
console.log(group);
console.log(arrangedProducts);
This is my expected output:
/*
expected result = [
[
{id: 1, name: 'Almendras', category: 'Fruit'},
{id: 2, name: 'Nueces', category: 'Fruit'},
{id: 6, name: 'Arandanos', category: 'Fruit'},
{id: 7, name: 'Maracuya', category: 'Fruit'}
],
[
{id: 0, name: 'Chocolate', category: 'Sweet'},
{id: 4, name: 'Alfajor', category: 'Sweet'}
],
[
{id: 5, name: 'Queso', category: 'UwU'}
]
]
*/
Solution
Making of groups
Apply settings and retrieve the result
const products = [
{ id: 0, name: "Chocolate", category: "Sweet" },
{ id: 1, name: "Almendras", category: "Fruit" },
{ id: 2, name: "Nueces", category: "Fruit" },
{ id: 3, name: "Mermelada", category: "Jam" },
{ id: 4, name: "Alfajor", category: "Sweet" },
{ id: 5, name: "Queso", category: "UwU" },
{ id: 6, name: "Arandanos", category: "Fruit" },
{ id: 7, name: "Maracuya", category: "Fruit" },
];
const productsGroup = products.reduce((r, a) => {
r[a.category] = [...(r[a.category] || []), a];
return r;
}, {});
function applySettings(settings) {
return settings.filter((s) => s.display).map((s) => productsGroup[s.name]);
}
console.log(
applySettings([
{
name: "Fruit",
display: true,
},
{
name: "Jam",
display: false,
},
])
);
console.log(
applySettings([
{
name: "Fruit",
display: true,
},
{
name: "Sweet",
display: true,
},
{
name: "UwU",
display: true,
},
])
);
You can filter your settings list based on the display property and then use Array.map to return a list of objects in products that match the category:
const products = [
{id: 0, name: 'Chocolate', category: 'Sweet'},
{id: 1, name: 'Almendras', category: 'Fruit'},
{id: 2, name: 'Nueces', category: 'Fruit'},
{id: 3, name: 'Mermelada', category: 'Jam'},
{id: 4, name: 'Alfajor', category: 'Sweet'},
{id: 5, name: 'Queso', category: 'UwU'},
{id: 6, name: 'Arandanos', category: 'Fruit'},
{id: 7, name: 'Maracuya', category: 'Fruit'}
];
const settings = [
{ name: 'Fruit', display: true },
{ name: 'Jam', display: false },
{ name: 'Sweet', display: true },
{ name: 'UwU', display: true }
];
const result = settings
.filter(c => c.display)
.map(c => products.filter(o => o.category == c.name));
console.log(result);
Note that this code does filter the products array for each settings value that has display:true, so may be slow for large arrays. However filter is pretty low overhead and testing with OP's sample data shows this to run 3x the speed of the reduce version; and with a larger products array (99 entries) to run 10x faster.
This should be pretty quick, because it continues on to the next iteration without executing the inner loop when display is false:
var products = [
{id: 0, name: 'Chocolate', category: 'Sweet'},
{id: 1, name: 'Almendras', category: 'Fruit'},
{id: 2, name: 'Nueces', category: 'Fruit'},
{id: 3, name: 'Mermelada', category: 'Jam'},
{id: 4, name: 'Alfajor', category: 'Sweet'},
{id: 5, name: 'Queso', category: 'UwU'},
{id: 6, name: 'Arandanos', category: 'Fruit'},
{id: 7, name: 'Maracuya', category: 'Fruit'}
];
let settings = [
{
name: 'Fruit',
display: true
},
{
name: 'Jam',
display: false
},
{
name: 'Sweet',
display: true
},
{
name: 'UwU',
display: true
}
];
function sortProducts(){
const r = [];
let i = -1;
for(let s of settings){
if(!s.display){
continue;
}
i++;
for(let o of products){
if(s.name === o.category){
if(r[i]){
r[i].push(o);
}
else{
r.push([o]);
}
}
}
}
return r;
}
console.log(sortProducts());

Javascript loop an object array and return a new array

To use react, I have an object array like this:
{
_id: 1,
items: [
{ goodName: "cake", amount: 10 },
{ goodName: "potato", amount: 11 },
{ goodName: "apple", amount: 15 }
]
}
{
_id: 2,
items: [
{ goodName: "cake", amount: 10 },
{ goodName: "potato", amount: 11 },
{ goodName: "apple", amount: 15 }
]
}
{
_id: 3,
items: [
{ goodName: "cake", amount: 10 },
{ goodName: "potato", amount: 11 },
{ goodName: "apple", amount: 15 }
]
}
Now, I want to loop through this object array,and return an array
containing the amount's accumulated value of each good.I want to use .map() methods to make it like this:
var value = items.map(function(item) {
var amounts = 0;
var amount=[];
for (var i=0; i<=myArr.length; i++) {
if (myArr[i] === item.name) {
amounts=amounts+item.amount;
amount.push(amounts);
}
}
})
But it doesn't work. myArr is an array I use the new Set() and
Array.from() method to loop through the goodName and return it. I don't know what method I use can make it, is everyone has another way?
I have written code to loop over your information and return tallies of the total amounts of each good type. However, several clarifications are required.
The information in your question needs to be wrapped in square brackets, as the outer objects you show need to be elements in an array.
I am returning an object, not an array as you suggest in your question. That allows the results to contain information about which items each tally corresponds to, i.e. {good1: total1, good2: total2, ...}
I include an extra (fourth) object just to show that the code requires neither the same number of goods nor even the same identities of goods in each array element.
var arr = [
{_id: 1, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 2, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 3, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 4, items: [
{goodName: "potato", amount: 1000},
{goodName: "peach", amount: 2000}
]}
];
var results = {};
arr.forEach(arrElmt => {
arrElmt.items.forEach(item => {
results[item.goodName] = (results[item.goodName] || 0) + item.amount;
});
});
var res = JSON.stringify(results);
console.log(res);
You can't use only map, because you are accumulating the values of the items. I would use reduce:
const arr = [
{_id: 1, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 2, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 3, items: [
{goodName: "cake", amount: 10},
{goodName: "potato", amount: 11},
{goodName: "apple", amount: 15}
]},
{_id: 4, items: [
{goodName: "potato", amount: 1000},
{goodName: "peach", amount: 2000}
]}
];
let results = [].concat.apply([], arr.map(elem => elem.items))
.reduce((results, item) => {
results[item.goodName] = (results[item.goodName] || 0) + item.amount;
return results;
}, {});
document.write(JSON.stringify(results));
JS does not have flatMap, so the [].concat.apply dance is a little weird, but any good library will have it.

Categories

Resources