Sorting large array into simpler one [duplicate] - javascript

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
Closed 4 years ago.
What would be the best way to sort myarr into sortedArr so that all of the values have been added together for each invidual id using javascript?
myarr = [{id: 10, val:100}, {id:10, val: 100}, {id:20, val:200}, {id:20, val:100}, {id:30, val:100}]
sortedArr = [{id: 10, val: 200}, {id:20, val:300}, {id:30, 100}]

First sum the values per ID, then push them to a new array.
let myarr = [{ id: 10, val: 100 }, { id: 10, val: 100 }, { id: 20, val: 200 }, { id: 20, val: 100 }, { id: 30, val: 100 }]
let group = {}
myarr.forEach((value, index) => {
if (group[value.id]) {
group[value.id] += value.val
}
else {
group[value.id] = value.val
}
})
let res = []
Object.keys(group).forEach((key) => {
res.push({ id: key, val: group[key] })
})
console.log(res);

You need to keep a map of keys (hits) and reduce the original array. You can sort it afterwards, because the array will be smaller which speeds up sorting.
var myArr = [{id: 10, val:100}, {id:10, val: 100}, {id:20, val:200}, {id:20, val:100}, {id:30, val:100}];
var sortedArr = accumulateAndSortById(myArr, 'id', 'val');
console.log(sortedArr);
function accumulateAndSortById(arr, field, value) {
var hitMap = arr.reduce((result, item) => {
result[item[field]] = (result[item[field]] || 0) + item[value];
return result;
}, {});
return Object.keys(hitMap).reduce((result, key) => {
var obj = {};
obj[key] = hitMap[key];
result.push(obj);
return result;
}, []).sort((a, b) => a[field] > b[field]);
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

You can do:
const myArr = [{id: 10, val:100}, {id:10, val: 100}, {id:20, val:200}, {id:20, val:100}, {id:30, val:100}]
const temp = myArr.reduce((a, c) => (a[c.id] = (a[c.id] || 0) + c.val, a), {});
const sortedArr = Object.keys(temp).map(k => ({id: +k, val: temp[k]}));
console.log(sortedArr);

Use the following compare function to sort:
var myArr = [{id: 10, val:100}, {id:10, val: 100}, {id:20, val:200}, {id:20, val:100}, {id:30, val:100}];
function compare(a,b) {
if (a.id < b.id)
return -1;
if (a.id > b.id)
return 1;
return 0;
}
var sortedArr = myArr.sort(compare);

Related

Compare two Arrays and Insert Null Vallues

I have two arrays:
var array1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
var array2 = [{id: 1, time: 100}, {id: 3, time: 300}];
And I would like for array2 to be changed to
var array2 = [{id: 1, time: 100}, null, {id: 3, time: 300}];
The question is how can I compare the two arrays and look at their time and then insert null in the missing locations for each array.
Any help is appreciated!
const arr1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
const arr2 = [{id: 1, time: 100}, {id: 3, time: 300}, {id: 3, time: 400}];
const uniqueTimes = [...arr1, ...arr2]
.filter((e, i, a) => a.findIndex(x => x.time === e.time) === i)
const res1 = uniqueTimes.map(e =>
arr1.find(x => x.time === e.time) ?? null
)
const res2 = uniqueTimes.map(e =>
arr2.find(x => x.time === e.time) ?? null
)
console.log(res1)
console.log(res2)
Your example is a little misleading. Your description of the prompt says entries can be missing in both arrays, right? My example has 200 missing in array2, and 400 missing in array1
var array1 = [{ id: 1, time: 100 }, { id: 2, time: 200 }, { id: 3, time: 300 }];
var array2 = [{ id: 1, time: 100 }, { id: 3, time: 300 }, { id: 1, time: 400 }];
// get all possible times, sort them
const allSortedTimes = array1.map(({ time }) => time).concat(array2.map(({ time }) => time)).sort((a, b) => a - b)
// only use uniq times
const allUniqTimes = [...new Set(allSortedTimes)]
// now that we have all the possible times,
// we go over each array and check to see if that time exists
const insertedArray1 = allUniqTimes.map((uniqTime) => {
return array1.find(({ time }) => time === uniqTime) ?? null
})
const insertedArray2 = allUniqTimes.map((uniqTime) => {
return array2.find(({time}) => time === uniqTime) ?? null
})
console.log(insertedArray1)
console.log(insertedArray2)
Here's one way to do it.
var array1 = [{
id: 1,
time: 100
}, {
id: 2,
time: 200
}, {
id: 3,
time: 300
}];
var array2 = [{
id: 1,
time: 100
}, {
id: 3,
time: 300
}];
const fixArray = (a, maxTime) => {
let inc = 100,
start = inc,
tmp = [];
// first make sure we have it in order
//a = a.sort((a, b) => (a.time < b.time) ? -1 : 1)
while (start < maxTime) {
let t = a.filter(el => el.time === start)
if (t.length === 0) tmp.push(null);
else tmp.push(t[0])
start += inc;
}
return tmp
}
array2 = fixArray(array2, 1000);
console.log(array2)
const getKey=(id,time)=>id+"_"+time //Provides unique key based on object values
var array1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
var array2 = [{id: 0, time: 5},{id: 1, time: 100},{id: 11, time: 250}, {id: 3, time: 300},{id: 5, time: 500}];
let keysOfArray1=[]//array1 unique keys
let keysOfArray2=[]//array2 unique keys
array1.map(item=>{
keysOfArray1.push(getKey(item.id,item.time)); // collects array1 unique keys
return item
}).concat(array2.map(item=>{//concat array2 values with array1
keysOfArray2.push(getKey(item.id,item.time)) // collects array2 unique keys
return item
})).sort((a,b)=>a.time-b.time || a.id-b.id).reduce((prev,current)=>{ //Sort by time & then Id
let keyName=getKey(current.id,current.time)
if(!prev.includes(keyName)){// To consider all objects only once
array1[prev.length]=keysOfArray1.includes(keyName)?current:null
array2[prev.length]=keysOfArray2.includes(keyName)?current:null
prev.push(keyName)
}
return prev
},[])
console.log(array1);
console.log(array2);

Sum array of objects values by multiple keys in array

Suppose I have:
const arr = [
{'name': 'P1','value': 150, 'nn': 2},
{'name': 'P1','value': 150, 'nn': 3},
{'name': 'P2','value': 200, 'nn': 5},
{'name': 'P3','value': 450, 'nn': 1}
]
const keysToSum = ['value', 'nn' ]
and I want:
[
{ name: 'P1', value: 300, nn: 5 },
{ name: 'P2', value: 200, nn: 5 },
{ name: 'P3', value: 450, nn: 1 }
]
So I want to sum the values inside the keys value and nn (because these keys are in keysToSum) if name is the same.
How can I do that?
I know I can use reduce, for example:
const result = arr.reduce((acc, val) => {
const o = acc.filter((obj) => {
return obj.name == val.name;
}).pop() || {name: val.name, value: 0};
o.value += val.value;
acc.push(o);
return acc;
},[])
But this piece of code works with only a key (value in that case), how can generalize it using keysToSum?
const arr = [
{'name': 'P1','value': 150, 'nn': 2},
{'name': 'P1','value': 150, 'nn': 3},
{'name': 'P2','value': 200, 'nn': 5},
{'name': 'P3','value': 450, 'nn': 1}
];
const result = arr.reduce((acc, val) => {
const o = acc.filter((obj) => {
return obj.name == val.name;
}).pop() || {name: val.name, value: 0};
o.value += val.value;
acc.push(o);
return acc;
},[]);
console.log(result)
Using reduce with an object that uses the name as a key, you can easily keep track of the objecs with dupe and increment the properties.
const arr = [
{'name': 'P1','value': 150, 'nn': 0},
{'name': 'P1','value': 150, 'nn': 3},
{'name': 'P2','value': 200, 'nn': 2},
{'name': 'P3','value': 450, 'nn': 5}
]
const keysToSum = ['value', 'nn' ]
const summed = Object.values(arr.reduce((obj, record) => {
// have we seen this name yet? If not copy the record
if (!obj[record.name]) {
obj[record.name] = { ...record };
} else {
// if we saw it, sum the fields we care about
keysToSum.forEach(key => {
obj[record.name][key] += record[key];
})
}
return obj
}, {}));
console.log(summed)
You could reduce the array by using an object and iterate the wanted keys for summing property values.
const
array = [{ name: 'P1', value: 150, nn: 0 }, { name: 'P1', value: 150, nn: 3 }, { name: 'P2', value: 200, nn: 2 }, { name: 'P3', value: 450, nn: 5 }],
keysToSum = ['value', 'nn'],
result = Object.values(array.reduce((r, { name, ...o }) => {
if (!r[name]) r[name] = { name };
keysToSum.forEach(k => r[name][k] = (r[name][k] || 0) + o[k]);
return r;
}, []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

How to create array of array by grouping ids

I have an array:
let array = [{id: 1, name:'a'}, {id: 2, name: 'b'},{id: 1, name:'c'}];
I want an array like
arr2 = [[{id: 1, name:'a'}, {id: 1, name:'c'}], [{id: 2, name: 'b'}]]
I tried this but I cant find which array to add to:
$scope.journalsArray = response.data.body.Data;
$scope.newArray = [];
let idsArray = [];
for (let i = 0; i < $scope.journalsArray.length; i++) {
if(idsArray.indexOf($scope.journalsArray[i].journal_id) != -1){
$scope.newArray.push($scope.journalsArray[i]);
}else{
$scope.idsArray.push($scope.journalsArray[i].journal_id);
}
}
Array.reduce over your original array.
If a group exists that has an item with the same id, push into it.
Otherwise create the group.
let array = [{id: 1, name:'a'}, {id: 2, name: 'b'},{id: 1, name:'c'}]
let result = array.reduce((acc, item) => {
const group = acc.find(group => group.find(inner => inner.id === item.id))
if (group)
group.push(item)
else
acc.push([item])
return acc
}, [])
console.log(result)
Using reduce(), find() and filter() methods we can achieve this
let array = [
{id: 1, name:'a'},
{id: 2, name: 'b'},
{id: 1, name:'c'}
];
const results = arr => {
return arr.reduce((result, item ) => {
if (!result.find(obj => obj[0].id === item.id)) {
result.push(array.filter(val => val.id == item.id))
}
return result;
}, [])
}
console.log(results(array));
// ​​​​​[ [ { id: 1, name: 'a' }, { id: 1, name: 'c' } ], [ { id: 2, name: 'b' } ] ]​​
You could take a Map and collect the objects in the same group. Then get the values of each group as result.
var array = [{ id: 1, name:'a' }, { id: 2, name: 'b' },{ id: 1, name:'c' }],
result = Array.from(
array
.reduce((m, o) => m.set(o.id, [...(m.get(o.id) || []), o]), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Array#reduce, Map, destructuring and Array#concat.
const data = [{id: 1, name:'a'}, {id: 2, name: 'b'},{id: 1, name:'c'}];
const res = data.reduce((a,{id,name})=>{
return a.set(id, [{id,name}].concat(a.get(id)||[]));
}, new Map()).values();
console.log([...res]);
let array = [{id: 1, name:'a'}, {id: 2, name: 'b'},{id: 1, name:'c'}]
let result = array.reduce((acc, item) => {
const group = acc.find(group => group.find(inner => inner.id === item.id))
if (group)
group.push(item)
else
acc.push([item])
return acc
}, [])
console.log(result)
let array = [{id: 1, name:'a'}, {id: 2, name: 'b'},{id: 1, name:'c'}]
let result = array.reduce((acc, item) => {
const group = acc.find(group => group.find(inner => inner.id === item.id))
if (group)
group.push(item)
else
acc.push([item])
return acc
}, [])
console.log(result)

Linear sorting of tree structured items [duplicate]

I would like to create a (non-anonymous) function that sorts an array of objects alphabetically by the key name. I only code straight-out JavaScript so frameworks don't help me in the least.
var people = [
{'name': 'a75', 'item1': false, 'item2': false},
{'name': 'z32', 'item1': true, 'item2': false},
{'name': 'e77', 'item1': false, 'item2': false}
];
How about this?
var people = [
{
name: 'a75',
item1: false,
item2: false
},
{
name: 'z32',
item1: true,
item2: false
},
{
name: 'e77',
item1: false,
item2: false
}];
function sort_by_key(array, key)
{
return array.sort(function(a, b)
{
var x = a[key]; var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
people = sort_by_key(people, 'name');
This allows you to specify the key by which you want to sort the array so that you are not limited to a hard-coded name sort. It will work to sort any array of objects that all share the property which is used as they key. I believe that is what you were looking for?
And here is a jsFiddle: http://jsfiddle.net/6Dgbu/
You can sort an array ([...]) with the .sort function:
var people = [
{'name': 'a75', 'item1': false, 'item2': false},
{'name': 'z32', 'item1': true, 'item2': false},
{'name': 'e77', 'item1': false, 'item2': false},
];
var sorted = people.sort(function IHaveAName(a, b) { // non-anonymous as you ordered...
return b.name < a.name ? 1 // if b should come earlier, push a to end
: b.name > a.name ? -1 // if b should come later, push a to begin
: 0; // a and b are equal
});
This isn't a JSON question, per se. Its a javascript array question.
Try this:
people.sort(function(a,b){
var x = a.name < b.name? -1:1;
return x;
});
My solution for similar sort problem using ECMA 6
var library = [
{name: 'Steve', course:'WAP', courseID: 'cs452'},
{name: 'Rakesh', course:'WAA', courseID: 'cs545'},
{name: 'Asad', course:'SWE', courseID: 'cs542'},
];
const sorted_by_name = library.sort( (a,b) => a.name > b.name );
for(let k in sorted_by_name){
console.log(sorted_by_name[k]);
}
I modified #Geuis 's answer by using lambda and convert it upper case first:
people.sort((a, b) => a.toLocaleUpperCase() < b.toLocaleUpperCase() ? -1 : 1);
Array.prototype.sort_by = function(key_func, reverse=false){
return this.sort( (a, b) => ( key_func(b) - key_func(a) ) * (reverse ? 1 : -1) )
}
Then for example if we have
var arr = [ {id: 0, balls: {red: 8, blue: 10}},
{id: 2, balls: {red: 6 , blue: 11}},
{id: 1, balls: {red: 4 , blue: 15}} ]
arr.sort_by(el => el.id, reverse=true)
/* would result in
[ { id: 2, balls: {red: 6 , blue: 11 }},
{ id: 1, balls: {red: 4 , blue: 15 }},
{ id: 0, balls: {red: 8 , blue: 10 }} ]
*/
or
arr.sort_by(el => el.balls.red + el.balls.blue)
/* would result in
[ { id: 2, balls: {red: 6 , blue: 11 }}, // red + blue= 17
{ id: 0, balls: {red: 8 , blue: 10 }}, // red + blue= 18
{ id: 1, balls: {red: 4 , blue: 15 }} ] // red + blue= 19
*/
var data = [ 1, 2, 5, 3, 1];
data.sort(function(a,b) { return a-b });
With a small compartor and using sort, we can do it
This is how simply I sort from previous examples:
if my array is items:
0: {id: 14, auctionID: 76, userID: 1, amount: 39}
1: {id: 1086, auctionID: 76, userID: 1, amount: 55}
2: {id: 1087, auctionID: 76, userID: 1, amount: 55}
I thought simply calling items.sort() would sort it it, but there was two problems:
1. Was sorting them strings
2. Was sorting them first key
This is how I modified the sort function:
for(amount in items){
if(item.hasOwnProperty(amount)){
i.sort((a, b) => a.amount - b.amount);
}
}
var library = [
{name: 'Steve', course:'WAP', courseID: 'cs452'},
{name: 'Rakesh', course:'WAA', courseID: 'cs545'},
{name: 'Asad', course:'SWE', courseID: 'cs542'},
];
const sorted_by_name = library.sort( (a,b) => a.name > b.name );
for(let k in sorted_by_name){
console.log(sorted_by_name[k]);
}
var library = [
{name: 'Steve', course:'WAP', courseID: 'cs452'},
{name: 'Rakesh', course:'WAA', courseID: 'cs545'},
{name: 'Asad', course:'SWE', courseID: 'cs542'},
];
const sorted_by_name = library.sort( (a,b) => a.name > b.name ? 1:-1 );
for(let k in sorted_by_name){
console.log(sorted_by_name[k]);
}
var people =
[{"name": 'a75',"item1": "false","item2":"false"},
{"name": 'z32',"item1": "true","item2": "false"},
{"name": 'e77',"item1": "false","item2": "false"}];
function mycomparator(a,b) { return parseInt(a.name) - parseInt(b.name); }
people.sort(mycomparator);
something along the lines of this maybe (or as we used to say, this should work).

Categories