Sort array and SUM item counts [duplicate] - javascript

I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.

You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce

A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example

You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);

JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});

You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);

You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>

Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0

Related

How can add properties to my objects based on the duplicates inside the array?

I have this array of objects
let arr = [
{
id: 1,
},
{
id: 1,
},
{
id: 2,
},
{
id: 1,
},
{
id:4,
},
{
id: 3,
},
{
id:4,
}
]
i need to find and change every object in the array based on condition.
So if there are duplicates in the array i need to set on my objects 100 except last duplicate where i should have 200.
If i don't have any duplicates than i should have again 200
So the output shpuld be
let arr = [
{
id: 1,
number: 100
},
{
id: 1,
number: 100
},
{
id: 2,
number: 200
},
{
id: 1,
number: 200
},
{
id:4,
number: 100
},
{
id: 3,
number: 200
},
{
id:4,
number: 200
}
]
so id 1 has duplicates.
That is why the fiurst occurences are set with number:100 and the last one i set with number:200.
Id 2 has number 200 because there are no duplicates and it is first occurance in the list.
what i tried
I got stuck at
for(let item of arr) {
for(let item2 of arr) {
if(item.id === item2.id) {
item.number = 100;
} else {
item.number = 200;
}
}
}
You can simply iterate through the array in reverse and track which ids you've seen, here using a Set.
const arr = [{ id: 1, }, { id: 1, }, { id: 2, }, { id: 1, }, { id: 4, }, { id: 3, }, { id: 4, }]
let i = arr.length;
const seen = new Set();
while (i--) {
arr[i].number = seen.has(arr[i].id) ? 100 : 200;
seen.add(arr[i].id)
}
console.log(arr)
You can use array.map() to iterate over your array. I think it can provide a nice and concise solution:
const result = arr.map((item, index) => {
const duplicate = arr.filter((_, indx) => indx > index).some((i) => i.id === item.id);
return { ...item, number: duplicate ? 100 : 200 }
});
console.log(result);
We can simply achieve it via Array.map() along with Array.indexOf() & Array.lastIndexOf() methods.
Working Demo :
// Input array
let arr = [{
id: 1,
}, {
id: 1,
}, {
id: 2,
}, {
id: 1,
}, {
id:4,
}, {
id: 3,
}, {
id:4,
}];
// Getting ID's from each object and create a seperate array
let idArr = arr.map(function(item) { return item.id });
// Iterating through the id's array and assigning number property to an original array as per the requirement.
idArr.forEach((item, index) => {
if (idArr.indexOf(item) === idArr.lastIndexOf(item)) {
arr[index].number = 200;
} else {
arr[index].number = 100;
arr[idArr.lastIndexOf(item)].number = 200;
}
});
// Result
console.log(arr);

nested for loops to get another object-array output

I need to multiply all the "values" inside "obj1" with the "percent' inside obj2 based on the id of each object. What would be the best way to do that? I've tried with for loop and reduce but I wasn't successful. Any help will be appreciated.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can do that by going through and matching the ids. there are some optimizations that can be made if they are sorted however.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
const x = Object.keys(obj2).map((key,index)=>{
const { id, value } = obj1.find(({id})=>id===obj2[key].id)
return ({id,value:value*obj2[key].percent})
})
console.log(x)
//outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can first create a lookup map using Map, then loop over the obj1 using map to get the desired result
const obj1 = [
{ id: 1, value: 10 },
{ id: 2, value: 10 },
];
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
},
};
const map = new Map();
Object.values(obj2).forEach((v) => map.set(v.id, v));
const result = obj1.map((o) => ({ ...o, value: o.value * map.get(o.id).percent }));
console.log(result);
This should work for you but doesnt handle exeptions if the id doesnt exist in both objects.
// First get the values in an array for easier manipulation
const aux = Object.values(obj2)
const output = obj1.map(ob => {
// Find the id in the other array.
const obj2Ob = aux.find(o => o.id === ob.id) // The id must exist in this aproach
return {
id: ob.id,
value: ob.value * obj2Ob.percent
}
})
console.log(output) // [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]

Update the value of nested object in the array based on the key

i have two array of objects like below, am trying to compare two arrays and looking to update the object total value of arr1 if the id matches with arr2.
const arr1 = [
{
id: 1,
value: { total: 0 },
},
{
id: 2,
value: { total: 0 },
},
{
id: 3,
value: { total: 0 },
},
id: 4,
value: { total: 0 },
},
];
const arr2 = [
{
id: 2,
value: 3 ,
},
{
id: 3,
value: 5,
},
];
I am trying to compare two arrays and looking to update the object total value of arr1 if the id matches with arr2
expected result is
const arr1 = [
{
id: 1,
value: { total: 0 },
},
{
id: 2,
value: { total: 3 },
},
{
id: 3,
value: { total: 5 },
},
{
id: 4,
value: { total: 0 },
},
];
I have tried the below code,
arr1.map((item) => {
arr2.find((element) => {
if (element.id === item.id ) {
item = {
...item,
value: {
...item.value,
total: item.value.total + element.value,
},
};
console.log(item);
}
});
return item;
});
I have changed the find function to the filter function and will add the result to the original array.
try this
const arr1 = [
{
id: 1,
value: { total: 0 },
},
{
id: 2,
value: { total: 0 },
},
{
id: 3,
value: { total: 0 },
},
{
id: 4,
value: { total: 0 },
}
]
const arr2 = [
{
id: 2,
value: 3 ,
},
{
id: 3,
value: 5,
},
];
arr1.map((item) => {
let result = arr2.filter((element) => element.id == item.id)
if(result && result.length) {
item.value.total += result[0].value
}
return item;
});
console.log(arr1)
You have to assign map to your array:
this.arr1 = this.arr1.map((item) => {
this.arr2.find((element) => {
if (element.id === item.id) {
item = {
...item,
value: {
...item.value,
total: item.value.total + element.value,
},
};
console.log(item);
}
});
return item;
});
If each object's id is unique (within each array), you can do:
arr1.forEach((obj1) => {
const obj2Match = arr2.find((obj2) => obj1.id === obj2.id );
if (obj2Match) {
obj1.value.total += obj2Match.value;
}
});
If more than one object can have the same id (e.g. arr2 has two objects that have the id of 2), then you can do:
arr1.forEach((obj1) => {
const obj2Matches = arr2.filter((obj2) => obj1.id === obj2.id );
if (obj2Matches.length) {
obj1.value.total += obj2Matches.reduce(((accum, curr) => accum + curr.value), 0);
}
});
Complexity:
Time: O(N * M)
Space: O(M)
N => arr1.length, M => arr2.length
You could do something like this:
arr2.map((x) => {
let result = arr1.filter((a1) => a1.id === x.id);
if (result.length > 0) {
x.total = result[0].total;
}
return x;
});

Filter array of object with conditions

I would like to filter an array of objects according to the highest value of "value" key and distinct each object by their "id" key.
Example :
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
]
And the expected output:
array = [
{
id: 1,
value: 10
},
{
id: 2,
value: 6
}
]
Thanks
You could reduce the array and check if an object exist with the same id and update if necessary. This approach takes the objects with the largest value.
const
array = [{ id: 1, value: 10 }, { id: 1, value: 2 }, { id: 2, value: 6 }, { id: 2, value: 5 }, { id: 2, value: 1 }],
result = array.reduce((r, o) => {
const index = r.findIndex(({ id }) => o.id === id);
if (index === -1) r.push(o);
else if (r[index].value < o.value) r[index] = o;
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another approach with a Map.
const
array = [{ id: 1, value: 10 }, { id: 1, value: 2 }, { id: 2, value: 6 }, { id: 2, value: 5 }, { id: 2, value: 1 }],
result = Array.from(
array.reduce((m, { id, value }) => m.set(id, m.has(id)
? Math.max(m.get(id), value)
: value
), new Map),
([id, value]) => ({ id, value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a possible way to doing this
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
]
let objdis = {};
array.forEach(val => {
if(objdis[val.id] !== undefined){
objdis[val.id].push(Number(val.value));
}else{
objdis[val.id] = [];
objdis[val.id].push(val.value);
}
})
let returningObj = [];
for (prop in objdis){
let obj = {};
obj.id = Number(prop);
obj.Value = Math.max(...objdis[prop])
returningObj.push(obj);
}
console.log(returningObj);
i hope this helps
var arr = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
];
const output = Object.values(arr.reduce((x, y) => {
x[y.id] = x[y.id] && x[y.id].value > y.value ? x[y.id] : y
return x
}, {}));
console.log(output);
There's my answer, but it needs to have sorted indexes and isn't as cool as reduce examples You got here.
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 1
},
{
id: 2,
value: 1
},
{
id: 2,
value: 5
},
{
id: 2,
value: 35
},
{
id: 3,
value: 4
},
{
id: 3,
value: 14
},
{
id: 2,
value: 123
}
];
function getHighestValues(Arr) {
var newArr = [];
var IdArr = Arr.map(elem => elem.id);
IdArr.sort((a,b)=>a-b);
for(var i = 0; i < Arr.length; i++) {
let currIdIndex = IdArr.indexOf(Arr[i].id);
if(newArr[currIdIndex] == undefined) {
newArr.push(
{
id: Arr[i].id,
value: Arr[i].value
})
} else if(newArr[currIdIndex].value < Arr[i].value) {
newArr[currIdIndex].value = Arr[i].value;
}
IdArr.splice(currIdIndex, 1);
if(IdArr.indexOf(Arr[i].id) == -1) {
IdArr.splice(currIdIndex, 0, Arr[i].id);
}
}
return newArr;
}
array = getHighestValues(array);
console.log(array);

Clean array of object depending from two propreties

I have an array of object like this:
const obj1 = [{
id: null,
val: 1
},{
id: 123,
val: 1
},{
id: 456,
val: 2
},{
id: null,
val: 3
}];
I need to check when property 'val' is never double, and in case one of those is double should keep the object that have the 'id' not to null. To explain me better the result of the array should be like:
[{
id: 123,
val: 1
},{
id: 456,
val: 2
},{
id: null,
val: 3
}];
Thanks for helping me with that.
There might be a shorter way, but you can definitely pull this off using a reduce:
const obj1 = [{
id: null,
val: 1
},{
id: 123,
val: 1
},{
id: 456,
val: 2
},{
id: null,
val: 3
}];
const result = obj1.reduce((res, item) => {
// Find index of item with same "val" if any.
const index = res.findIndex(x => x.val === item.val)
if (res[index]) {
// If there was item with same "val", but null ID, replace it, otherwise do nothing:
if (res[index].id === null) res[index] = item;
} else {
// Otherwise just add to array.
res.push(item);
}
return res;
}, []);
console.log(result)
You could use a Map and check if the value is not in the map or if the id is null, then set the map with the actual object.
var array = [{ id: null, val: 1 }, { id: 123, val: 1 }, { id: 456, val: 2 }, { id: null, val: 3 }],
map = new Map(),
result;
array.forEach(o => (!map.has(o.val) || map.get(o.val).id === null) && map.set(o.val, o));
result = [...map.values()];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources