JavaScript - reduce array - javascript

Working with an array of objects like:
[
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: 'American BBQ DOG',
extra_id: 1,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Mustard (60 g)',
},
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: 'American BBQ DOG',
extra_id: 3,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Roasted onions (30 g)',
}
]
I am trying to reduce the array on matching values for the orderItemId key and achieve an desired output like:
const desired = [
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: 'American BBQ DOG',
extra: [
{
extra_id: 1,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Mustard (60 g)',
},
{
extra_id: 3,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Roasted onions (30 g)',
}
]
}
];
Any ideas? I tried several logic but failed.

Here's one way to do it. It just uses Array.find() to find a previous item with the same orderItemId, if found, appends the extras to its extra array. If the original array is very large, consider using an object or a Map as the accumulator passed to the .reduce call and using Object.values() later.
If you need to handle extra logic such as adding product_quantity or deduplicating extra, you'll need to handle that as well.
const arr = [
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: "American BBQ DOG",
extra_id: 1,
extra_quantity: 3,
extra_price: 2,
extra_name: "Mustard (60 g)",
},
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: "American BBQ DOG",
extra_id: 3,
extra_quantity: 3,
extra_price: 2,
extra_name: "Roasted onions (30 g)",
},
];
const reduced = arr.reduce((acc, val) => {
const {
extra_id,
extra_quantity,
extra_price,
extra_name,
...otherFields
} = val;
const existing = acc.find((item) => item.orderItemId === val.orderItemId);
if (!existing) {
acc.push({
...otherFields,
extra: [
{
extra_id,
extra_quantity,
extra_price,
extra_name,
},
],
});
return acc;
}
existing.extra.push({ extra_id, extra_quantity, extra_price, extra_name });
return acc;
}, []);
console.log(reduced);
Edit: Here's a version that uses an object as a lookup so it doesn't have to go through the entire accumulated array each time. The acc (accumulator) object is basically just an object where the property keys are the orderItemId and the value is the new merged object with that key.
const arr = [
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: "American BBQ DOG",
extra_id: 1,
extra_quantity: 3,
extra_price: 2,
extra_name: "Mustard (60 g)",
},
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: "American BBQ DOG",
extra_id: 3,
extra_quantity: 3,
extra_price: 2,
extra_name: "Roasted onions (30 g)",
},
];
const reducedObject = arr.reduce((acc, val) => {
const {
extra_id,
extra_quantity,
extra_price,
extra_name,
...otherFields
} = val;
const existing = acc[val.orderItemId];
if (!existing) {
acc[val.orderItemId] = {
...otherFields,
extra: [
{
extra_id,
extra_quantity,
extra_price,
extra_name,
},
],
};
return acc;
}
existing.extra.push({ extra_id, extra_quantity, extra_price, extra_name });
return acc;
}, {});
const reduced = Object.values(reducedObject);
console.log(reduced);

With this approach you can use it with different data structure also, whitout declare fields names (or key names).
In practice comparison and aggregation.
var alist=[
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: 'American BBQ DOG',
extra_id: 1,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Mustard (60 g)',
},
{
orderItemId: 180,
product_id: 1,
product_quantity: 3,
product_price: 18,
product_name: 'American BBQ DOG',
extra_id: 3,
extra_quantity: 3,
extra_price: 2,
extra_name: 'Roasted onions (30 g)',
}
];
function jS(o){
return JSON.stringify(o);
}
function equals(a,b){
var aa = jS(a);
var bb = jS(b);
return aa == bb;
}
function hLen(o){
return Object.keys(o).length;
}
function hKeys(h){
return Object.keys(h);
}
function reduce(arr){
var ha=arr[0];
var hb=arr[1];
var hc={};
var xc1={}; //extra 1 when are different
var xc2={}; //extra 2 when are different
maxhlen = Math.max(hLen(ha),hLen(hb));
akeys = hKeys(ha);
bkeys = hKeys(hb);
for(i=0;i<maxhlen;i++){
k=akeys[i];
if (ha[k]==hb[k]){
hc[k]=ha[k];
}else{
xc1[k]=ha[k];
xc2[k]=hb[k];
}
}
hc["extra"]=[xc1,xc2];
return hc;
}
res=reduce(alist);
console.log(res);

Related

How to check with an array have some values

How to verify with i have only 2 or 3 numbers inside this?
without this ----> if(Array.includes(1) && !Array.includes(3))
const servicesTest: IServices[] = [
{
id: '1',
name: 'Hair',
price: 25,
icon: 'https://cdn-icons-png.flaticon.com/512/7478/7478480.png'
},
{
id: '2',
name: 'Beard',
price: 20,
icon: 'https://cdn-icons-png.flaticon.com/512/7578/7578754.png'
},
{
id: '3',
name: 'Eyebrow',
price: 15,
icon: 'https://cdn-icons-png.flaticon.com/512/2821/2821012.png'
}
]
if the client choose hair + beard this will be 40 not 45.
I´m doing this:
const name = findServices.map(services => services.name)
if (name.includes('Hair') && name.includes('Beard') && !name.includes('Eyebrown')) {
return (
setTotalDay(prevState => prevState + 40),
setTotalMonth(prevState => prevState + 40)
)
}
I would create an array of discounts like this:
const discounts = [{
price: 30,
ids: [1, 2],
}];
Then check if the array has only discounted items like this:
array.length === discount.ids.length && array.every((item) => discount.ids.includes(item.id))
const discounts = [{
price: 30,
ids: [1, 2],
}];
const discounted = [{
id: 1,
name: 'Hair',
price: 20,
},
{
id: 2,
name: 'Beard',
price: 30,
},
];
const fullPrice = [{
id: 1,
name: 'Hair',
price: 20,
},
{
id: 2,
name: 'Beard',
price: 30,
},
{
id: 3,
name: 'Tea',
price: 30,
},
];
console.log("discounted", getTotal(discounted));
console.log("full price", getTotal(fullPrice));
function getTotal(array) {
for (const discount of discounts) {
if (
array.length === discount.ids.length &&
array.every((item) => discount.ids.includes(item.id))
) {
return discount.price;
}
}
return array.reduce((sum, item) => sum + item.price, 0);
}
answering your question before the edit.
Assuming we have this array
const Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Let's say we want to check if values 2 and 3 exist.
We store the values in an array let toCheck = [2,3];
We can use function every to loop all the elements of toCheck array against the Array const
Example Follows:
const Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let toCheck = [1,2];
const allExist = toCheck.every(value => {
return Array.includes(value);
});
Hope it helps.

How to put dynamic data under a array of objects?

This is how I get the data:
const dataSaved = [{
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "aslkasad",
subtotal: 2,
taxAmount: 4,
total: 3
}, {
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "ldfgfgdf",
subtotal: 2,
taxAmount: 4,
total: 3
}]
So basically I have made a method in which I'm filtering an array of objects and just taking out the fields I need:
getDataParsed(dataSaved, storeFieldRequired) {
const barData = [];
for (const store of dataSaved) {
barData.push({
data: [store.storeFieldRequired],
label: store.storeId
});
}
return barData;
}
When I want to get an specific field of the array, my [store.storeFieldRequired] brings undefined.
How can I solve it?
Instead of using store.storeFieldRequired, it is needed to use store[storeFieldRequired]. They have different meanings.
store.storeFieldRequired means to get the value of storeFieldRequired key on store object.
store[storeFieldRequired] means to get the value of storeFieldRequired variable value key on store object.
const dataSaved = [{
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "aslkasad",
subtotal: 2,
taxAmount: 4,
total: 3
}, {
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "ldfgfgdf",
subtotal: 2,
taxAmount: 4,
total: 3
}];
function getDataParsed(dataSaved, storeFieldRequired) {
const barData = [];
for (const store of dataSaved) {
barData.push({
data: [store[storeFieldRequired]],
label: store.storeId
});
}
return barData;
}
console.log(getDataParsed(dataSaved, 'count'));
Simply, using Array.prototype.map, you can get the result.
const dataSaved = [{
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "aslkasad",
subtotal: 2,
taxAmount: 4,
total: 3
}, {
count: 52,
deliveryAmount: 0,
discountAmount: 7,
guests: 2,
refundedAmount: 9,
serviceChargeAmount: 4,
storeId: "ldfgfgdf",
subtotal: 2,
taxAmount: 4,
total: 3
}];
function getDataParsed(dataSaved, storeFieldRequired) {
return dataSaved.map((item) => ({
data: [item[storeFieldRequired]],
label: item.storeId
}));
}
console.log(getDataParsed(dataSaved, 'count'));
Access the variable with bracket-[] notation. Example => data: [store[storeFieldRequired]].
getDataParsed(storeData, storeFieldRequired) {
const barData = [];
for (const store of storeData) {
barData.push({
data: [store[storeFieldRequired]], // change this line from data: [store.storeFieldRequired] to data: [store[storeFieldRequired]]
label: store.storeId
});
}
return barData;
}

javascript: changing order of values for all keys

I have an object with the following structure:
object = { ids: [], labels:[], values:[] }
each key has 80 values. I want to sort the 'values' key in descending order (biggest to smallest), and update the ids and labels keys accordingly (so the label and id of the highest 'values' value is now first). I tried the following code but it doesn't work:
function getSorted(arr, sortArr) {
var result = [];
for(var i=0; i<arr.length; i++) {
console.log(sortArr[i], arr[i]);
result[i] = arr[sortArr[i]];
}
return result;
}
the data I am trying to sort is this:
ids = [41, 121, 159, 165, 170, 189, 258, 259, 307, 340, 342, 352, 357, 412, 482, 513, 725, 830, 833, 874, 907, 944, 1167, 1169, 1189, 1193, 1208, 1232, 1274, 1314, 1497, 1498, 1503, 1505, 1795, 1950, 1959, 1960, 1962, 1968, 1977, 2011, 2024, 2039, 2065, 2077, 2110, 2167, 2178, 2184, 2186, 2188, 2191, 2235, 2244, 2247, 2264, 2275, 2291, 2318, 2335, 2342, 2350, 2396, 2419, 2475, 2483, 2491, 2546, 2571, 2722, 2737, 2739, 2782, 2811, 2859, 2908, 2936, 2964, 3450]
values = [71, 2, 2, 12, 2, 47, 2, 2, 7, 2, 2, 50, 2, 2, 113, 2, 2, 10, 3, 36, 3, 19, 163, 2, 51, 6, 5, 2, 4, 3, 2, 2, 2, 2, 10, 25, 30, 2, 3, 2, 40, 11, 13, 5, 2, 23, 2, 5, 7, 19, 3, 2, 28, 2, 14, 11, 78, 22, 2, 40, 3, 2, 2, 11, 13, 2, 2, 2, 2, 2, 8, 4, 4, 12, 13, 126, 7, 3, 10, 37]
labels = ["Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales;Actinomycetaceae", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales;Actinomycetaceae;Varibaculum", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales;Corynebacteriaceae", "Bacteria;Actinobacteria;Actinobacteria;Actinomycetales;Corynebacteriaceae;Corynebacterium", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Porphyromonadaceae;Porphyromonas", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Prevotellaceae", "Bacteria;Bacteroidetes;Bacteroidia;Bacteroidales;Prevotellaceae;Prevotella", "Bacteria;Firmicutes", "Bacteria;Firmicutes", "Bacteria;Firmicutes", "Bacteria;Firmicutes", "Bacteria;Firmicutes;Bacilli;Bacillales;Staphylococcaceae;Staphylococcus", "Bacteria;Firmicutes;Clostridia", "Bacteria;Firmicutes;Clostridia", "Bacteria;Firmicutes;Clostridia", "Bacteria;Firmicutes;Clostridia", "Bacteria;Firmicutes;Clostridia", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Anaerococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Finegoldia", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Gallicola", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Gallicola", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Peptoniphilus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Peptoniphilus", "Bacteria;Firmicutes;Clostridia;Clostridiales;IncertaeSedisXI;Peptoniphilus", "Bacteria;Firmicutes;Clostridia;Clostridiales;Peptococcaceae;Peptococcus", "Bacteria;Firmicutes;Clostridia;Clostridiales;Ruminococcaceae", "Bacteria;Firmicutes;Clostridia;Clostridiales;Veillonellaceae", "Bacteria;Proteobacteria;Epsilonproteobacteria;Campylobacterales;Campylobacteraceae;Campylobacter"]
How should I tackle this problem? Thanks
The only relationship you have between the three sets of data is the index inside of each array, which will be lost when you sort. To keep the three sets of data paired, I would map over your data and create an object for each:
const data = { ids, labels, values };
const list = data.values.map((value, idx) => {
return {
id: data.ids[idx],
value,
label: data.labels[idx]
};
});
Now list contains an array of objects:
[
{
id: 41,
label: 'Bacteria',
value: '71',
},
{
// ... etc
}
]
This is a much easier data structure to work with because you want id, label, and value to remain paired together.
At that point, you can easily sort this array of objects by the value property descending like so:
list.sort((a, b) => {
return b.value - a.value;
});
And now list is an array of objects of your data, sorted by object.value descending.
You can use this array of objects, or translate it back into your original format of three different arrays easily.
I know it's a little different from what you asked, but maybe you can have an idea on how to adapt for your needs.
While ago I made this function to solve a sorting problems on my objects.
function sortObj(obj, index, sorting) {
let ans = [];
sorting = sorting.toUpperCase();
if (obj.constructor === Array) {
for (let i=0; i < obj.length; i++) {
ans.push(obj[i]);
}
}
else {
for (let k in obj) {
if (obj.hasOwnProperty(k)) {
ans.push(obj[k])
}
}
}
if (!ans.length)
return [];
if (isNaN(ans[0][index])) {
ans = ans.sort(function (a,b) {
if (a[index]) {
if (sorting === "ASC")
return a[index].localeCompare(b[index]);
else
return b[index].localeCompare(a[index]);
}
return [];
});
}
else {
ans = ans.sort(function (a, b) {
if (sorting === "ASC")
return a[index] - b[index];
else
return b[index] - a[index];
})
}
return ans;
}
// structure of object
obj = {"1": {id: "1", label: "A", value: "5"}, "2": {id: "2", label: "B", value: "1"}}
sorted = sortObj(obj, "label", "ASC");
// [{id: "1", label: "A", value: "5"}, {id: "2", label: "B", value: "1"}]
sorted = sortObj(obj, "value", "ASC");
// [{id: "2", label: "B", value: "1"}, {id: "1", label: "A", value: "5"}]

groupBy array of objects with underscore.js

I have a array (order) of objects (products): , I want to group and count all these products by id:
var order = [
{ product_id: 70, product_price: 9, product_color: "ccc"},
{ product_id: 70, product_price: 9, product_color: "ccc"},
{ product_id: 71, product_price: 10, product_color: "fff" } ];
with Underscore.js:
var groups = _.countBy(order, function(value){
return value.product_id + "#" + value.product_price + "#" + value.product_color;
})
//--> Object {70#ccc#9: 2, 71#fff#10: 1}
So it works… but now, how can I return theses values into an array like this, so I can work with this as a new array of objects?
[
{ product_id: 70, product_price: 9, product_color: "ccc", count: 2},
{ product_id: 70, product_price: 9, product_color: "fff", count: 1}
];
Instead of countBy you could use groupBy and then map across the groups to add the count:
var groups = _.groupBy(order, function(value){
return value.product_id + "#" + value.product_price + "#" + value.product_color;
})
groups = _.map(groups, function(group){
return _.extend(group[0], {count: group.length});
});
You can use reduce to recreate the original array.
var countedById = [
{ product_id: 70, product_price: 9, product_color: "ccc", count: 2},
{ product_id: 70, product_price: 9, product_color: "fff", count: 1}
];
var original = countedById.reduce((acc, cur) => {
for (var i = 0; i < cur.count; i++) {
var original = {
product_id: cur.product_id,
product_price: cur.product_price,
product_color: cur.product_color
}
acc.push(original);
}
return acc;
}, []);
document.write(JSON.stringify(original))

Using an iterator function in Underscore's _.uniq

I've looked at this Stack question, "Removing duplicate objects with Underscore for Javascript" and that is exactly what I am trying to do, but none of the examples work. In fact I can not get any iterator function to work with _.uniq.
_.uniq([1, 2, 1, 3, 1, 4]);
> [1, 2, 3, 4]
_.uniq([1, 2, 1, 3, 1, 4], false, function(a){ return a===4;});
> [1, 2, 3, 4]
_.uniq([1, 2, 1, 3, 1, 4], true, function(a){ return a===4;});
> [1, 2, 1, 3, 1, 4]
_.uniq([1, 2, 1, 3, 1, 4], false, function(a){ return false;});
> [1, 2, 3, 4]
_.uniq([1, 2, 1, 3, 1, 4], false, function(a){ return true;});
> [1, 2, 3, 4]
var people = [ { name: 'John', age: 20 }, { name: 'Mary', age: 31 }, { name: 'Kevin', age: 20 }];
_.uniq(people, false, function(p){ return p.age; });
> [ { age: 20, name: "John" },
{ age: 31, name: "Mary" },
{ age: 20, name: "Kevin" } ]
I would do:
_.uniq(_.map(people, function(p){ return p.age; }));
> [20, 31]
but it returns only the mapped value, not the original object.
Any help appreciated. I am using underscore version 1.1.7
I had the same problem. It is caused because _.uniq() returns a new reduced array so you have to assing it to a variable. So with this little correction it has to work.
var people = [ { name: 'John', age: 20 }, { name: 'Mary', age: 31 }, { name: 'Kevin', age: 20 }];
people = _.uniq(people, false, function(p){ return p.age; });
[ { age: 20, name: "John" },
{ age: 31, name: "Mary" } ]
Looks like comparison functions for _.uniq were introduced in 1.2.0
from the changelog:
_.uniq can now be passed an optional iterator, to determine by what criteria an object should be considered unique.

Categories