I have an array:
const products = [
{ product: 'banana', price: 3 },
{ product: 'mango', price: 6 },
{ product: 'potato', price: ' ' },
{ product: 'avocado', price: 8 },
{ product: 'coffee', price: 10 },
{ product: 'tea', price: '' },
]
And Iwould like to sum all prices. What I tried:
const sum = products.reduce(function(acc, cur){
if (Number.isInteger(cur.price))
return acc+cur.price
}, 0)
console.log(sum)
it returns undefined. I also tried that without the condition, it returns a string. Where do I make a mistake?
You need to return the accumulator if the condition is not true.
const
products = [{ product: 'banana', price: 3 }, { product: 'mango', price: 6 }, { product: 'potato', price: ' ' }, { product: 'avocado', price: 8 }, { product: 'coffee', price: 10 }, { product: 'tea', price: '' }],
sum = products.reduce(function(acc, cur) {
if (Number.isInteger(cur.price)) return acc + cur.price;
else return acc;
}, 0);
console.log(sum);
A shorter approach adds the value conditionally and returns only the accumulator at the end.
const
products = [{ product: 'banana', price: 3 }, { product: 'mango', price: 6 }, { product: 'potato', price: ' ' }, { product: 'avocado', price: 8 }, { product: 'coffee', price: 10 }, { product: 'tea', price: '' }],
sum = products.reduce(function(acc, cur) {
if (Number.isInteger(cur.price)) acc += cur.price;
return acc;
}, 0);
console.log(sum);
Try this:
const products = [
{ product: 'banana', price: 3 },
{ product: 'mango', price: 6 },
{ product: 'potato', price: ' ' },
{ product: 'avocado', price: 8 },
{ product: 'coffee', price: 10 },
{ product: 'tea', price: '' },
]
const sum = products.filter(x => typeof x.price === 'number').map(x => x.price).reduce((a, b) => a + b);
console.log(sum)
The result of your accumulator for the last element tea is undefined because you don't return anything, if price is not a number. And as this is the last call to the accumulator, also the result of reduce is undefined.
If you just want to ignore values with invalid price, you can either use
products.filter(x => Number.isInteger(x.price)).reduce (...)
to only sum up products with a valid price
or return for instance acc if cur.price is not a number.
const sum = products.reduce(function(acc, cur){
if (Number.isInteger(cur.price))
return acc+cur.price;
return acc;
}, 0)
console.log(sum)
I want to find the common elements of multiple array of objects based on a common property. In addition, if an element appears more than once, I want the resulting array to reflect the number of times it occurs in all the arrays.
I tried the following:
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
function findArraysWithCommonName(arr) {
let arrays = [...arr];
var result = arrays.shift().reduce(function(res, v) {
if (arrays.every(function(a) {
return (a.filter(function(e) {
return e.name === v.name
}).length > 0);
})) res.push(v);
return res;
}, []);
return result;
}
console.log(findArraysWithCommonName(arr))
The result I got is:
[
{name: "apple", value: 5},
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
I expect the output to be:
[
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
or
[
{name: "apple", value: 5},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
One approach would be to build a map that relates an object to it's "count" in the array (ie the number of times that object occours in arr).
This can be done via .reduce() where you serialize each object to a string via JSON.stringify(obj) - this string is a unique encoding of the corresponding object shape and state which is used as the key to identify the objects of this form in the mapping. The key is used to query and update the "count" value of the mapping, for each object encountered in the arr.
Once the mapping has been build, filter mapping entries by those with a "count" value greater than one.
Finally for any filtered entries, deserialize the corresponding keys of those entries via .map() to obtain an array of objects that occoured more that one in the original arr.
This approach could be implemented as:
var arr=[[{name:'kiwi',value:12},{name:'apple',value:5},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'milk',value:5},{name:'banana',value:7},{name:'orange',value:11}],[{name:'taco',value:23},{name:'pizza',value:78},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'pie',value:1},{name:'cake',value:3},{name:'banana',value:7},{name:'beef',value:123},{name:'lime',value:72},{name:'pizza',value:34}],[{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'pizza',value:23},{name:'fish',value:5},{name:'banana',value:7},{name:'banana',value:77}]];
/* Flatten array heirachy */
const flatArr = arr.flat();
/* Obtain a count mapping for each object's occourance in flatArr */
const mapObjectToCount = flatArr.reduce((map, item) => {
const key = JSON.stringify(item);
const count = (map[key] ? map[key] : 0) + 1;
return { ...map, [ key ] : count };
}, {})
/* Get key/value pair of the prior mapping, filter the objects by
those that occour more that one time, and obtain the original object
by parsing the key */
const result = Object.entries(mapObjectToCount)
.filter(([json, count]) => count > 1)
.map(([json]) => JSON.parse(json));
console.log(result)
I'd first transform each subarray into an object indexed by the number of occurences of each name. Then, iterate through each of those sub-objects created, creating a new object whose values are the minimum of the values found on the combined object, for every key.
Lastly, return a .filter of the first array, checking whether the occurence count of the name being iterated over on that object is greater than 0, reducing that count by one when found:
function findArraysWithCommonName(arr) {
const [oneArr, ...rest] = arr;
/* Transform each subarray into, eg:
{
"taco": 1,
"pizza": 4,
"apple": 1,
"fish": 1,
"pie": 1,
...
*/
const countsByName = rest.map(
subarr => subarr.reduce((a, { name }) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {})
);
/* Combine the objects into one that contains only the minimum value for each property, eg:
{
"apple": 1,
"pizza": 3,
"fish": 1,
"banana": 1
}
*/
const combinedCountsByName = countsByName.reduce((a, countObj) => {
Object.entries(countObj).forEach(([key, val]) => {
countObj[key] = Math.min(a[key], val) || 0;
});
return countObj;
});
console.log(combinedCountsByName);
return oneArr.filter(({ name }) => {
if (combinedCountsByName[name] > 0) {
combinedCountsByName[name]--;
return true;
}
});
}
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
console.log(findArraysWithCommonName(arr));
I've been working on this couple hours and google around, see lots of example for remove duplicate, but not combined the value. so I hope someone can help me out here.
I want to check the item.name is the same, then add the price together then push to new list array.
const items = [
{ name: 'apple', price: '10' },
{ name: 'banana', price: '1' },
{ name: 'orange', price: '2' },
{ name: 'apple', price: '5' },
{ name: 'orange', price: '2.5' },
{ name: 'banana', price: '3' },
{ name: 'strawberry', price: '7' },
{ name: 'apple', price: '12' }
]
let newItem = []
const checkItem = items.map((prev, next) => {
if (prev.name === next.name) {
return newItem.push = {
name: next.name,
value: parseInt(prev.price) + parseInt(next.price)
}
}
});
console.log(newItem)
Big thanks for the help!
This will work.You can use reduce with Find.
const items = [{
name: 'apple',
price: '10'
},
{
name: 'banana',
price: '1'
},
{
name: 'orange',
price: '2'
},
{
name: 'apple',
price: '5'
},
{
name: 'orange',
price: '2.5'
},
{
name: 'banana',
price: '3'
},
{
name: 'strawberry',
price: '7'
},
{
name: 'apple',
price: '12'
}
]
let result = items.reduce((acc, el) => {
if (acc.filter(ele => ele.name == el.name).length == 0) {
acc.push(el);
} else {
let filtered = acc.find(ele => ele.name == el.name)
filtered.price = parseFloat(filtered.price) + parseFloat(el.price);
}
return acc;
}, [])
console.log(result)
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
The Array.prototype.map()'s callback functions first two arguments are currentValue i.e item of the array and second value is it's index, & not prev and next elements.
What you are looking for is something like this.
const items = [
{ name: "apple", price: "10" },
{ name: "banana", price: "1" },
{ name: "orange", price: "2" },
{ name: "apple", price: "5" },
{ name: "orange", price: "2.5" },
{ name: "banana", price: "3" },
{ name: "strawberry", price: "7" },
{ name: "apple", price: "12" }
];
const combine = items.reduce((acc, item) => {
if (acc[item.name] !== undefined) {
acc[item.name] += Number(item.price);
} else acc[item.name] = Number(item.price);
return acc;
}, {});
const fruitKeys = Object.keys(combine);
newItem = fruitKeys.map(item => ({ name: item, price: combine[item] }));
console.log(newItem);
I have split the solution into two steps, namely combine and reconstruction of the object so that you can clearly see what's happening.
I highly recommend you to refer the documentation for reduce method to understand its working
This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 3 years ago.
I'm trying to sort array of objects by "name" key. Here some values starts with 'summer', after sorting need to place those objects to be end of the list. Have any idea look this sorting once.
This is my list:
var list = [
{ name: 'Summer_Mango', id: 20055 },
{ name: 'Orange', id: 20053 },
{ name: 'Apple', id: 45652 },
{ name: 'Grape', id: 20066 },
{ name: 'Summer_Watermelon', id: 20073 },
{ name: 'Banana', id: 20010 }
];
After sorting my list need to come like this
var list = [
{ name: 'Apple', id: 45652 },
{ name: 'Banana', id: 20010 }
{ name: 'Grape', id: 20066 },
{ name: 'Orange', id: 20053 },
{ name: 'Summer_Mango', id: 20055 },
{ name: 'Summer_Watermelon', id: 20073 },
];
You can customize sort condition for your requirement.
list = list.sort((a, b) => {
if (a.name.startsWith('Summer')) {return 1; }
if (b.name.startsWith('Summer')) {return -1;}
return a.name.localeCompare(b.name)
});
var list = [
{ name: 'Summer_Mango', id: 20055 },
{ name: 'Orange', id: 20053 },
{ name: 'Apple', id: 45652 },
{ name: 'Grape', id: 20066 },
{ name: 'Summer_Watermelon', id: 20073 },
{ name: 'Banana', id: 20010 }
];
list = list.sort((a, b) => {
if (a.name.startsWith('Summer')) {return 1; }
if (b.name.startsWith('Summer')) {return -1;}
return a.name.localeCompare(b.name)
});
console.log(list)
Input:
const data = [
{
id: 'RS11',
name: 'Road 1',
quantity: {
lengthVal: 50
}
},
{
id: 'RS11',
name: 'Road 1',
quantity: {
lengthVal: 100
}
},
{
id: 'RS11',
name: 'Road 2',
quantity: {
lengthVal: 20
}
}
]
Each property except quantity should be grouped. quantity.lengthVal should be summarized. A count property should also be added.
Expected output:
const expected = [
{
id: 'RS11',
name: 'Road 1',
count: 2,
summarizedLength: 150
},
{
id: 'RS11',
name: 'Road 2',
count: 1,
summarizedLength: 20
}
]
This is what i tried:
const groupAndSum = (arr) => {
return _.chain(arr)
.groupBy((obj) => {
// Some reduce here?
return _.values(_.without(obj), 'quantity').join('|')
})
.map((val) => {
val[0].count = val.length;
delete val[0].quantity;
return val[0];
})
.value();
}
Jsbin: https://jsbin.com/totujopume/edit?html,js,console
Dataset is quite big so performance is important.
Can do this with a fairly simple native reduce() that only makes one iteration through the array for whole procedure
const res = Object.values(
data.reduce((a, c) => {
const key = c.id + '|' + c.name;
const o = a[key] = a[key] || c;
o.count = (o.count || 0) +1;
o.summarizedLength = (o.summarizedLength || 0) + c.quantity.lengthVal;
delete c.quantity;
return a;
},{})
);
console.log(res)
.as-console-wrapper { max-height: 100%!important;}
<script>
const data = [{
id: 'RS11',
name: 'Road 1',
quantity: {
lengthVal: 50
}
},
{
id: 'RS11',
name: 'Road 1',
quantity: {
lengthVal: 100
}
},
{
id: 'RS11',
name: 'Road 2',
quantity: {
lengthVal: 20
}
}
]
</script>