JavaScript: Array of Objects - Sorting by Attribute Value - javascript

I have an array of objects. I don't know how many objects there will be until the code is being run (it is returned from an API), but let's assume this is the array:
var arr = [ { A: 40, B: 88, C: 11 },
{ A: 10, B: 98, C: 65 }, // sum of A = 188
{ A: 11, B: 15, C: 18 }, // sum of B = 310
{ A: 16, B: 55, C: 16 }, // sum of C = 136
{ A: 22, B: 23, C: 13 },
{ A: 89, B: 31, C: 13 } ]
I want to look over every object in the array. I'd like the end-result to be a list of keys and values, sorted in descending order. So, if we were to use the array above, the code would return something like this:
[["B", 310], ["A", 188], ["C", 136]]
I hope it's not too much to ask if you can add comments in your code, as sometimes the answers here can be very short and efficient (and work brilliantly) but difficult to understand for a beginner with algorithms :)
Many thanks in advance.
EDIT: Each object does not necessarily always have three keys, it is around 30-40 keys.
BIT MORE INFO: I'm writing this for a stacked bar chart where I want to then only extract the top 10 keys and bucket the rest of the values into an "Others" key, but this is irrelevant to the question and only here for information.

If you are about to get sorted result -like you mentioned in your terms- for your complete array then this may be the answer.
You can first calculate sum of corresponding properties of every object in the array with a simple Array.prototype.reduce then convert the result to the structure you want (I preferred to loop over object keys) and then sort your structured array.
var arr = [ { A: 40, B: 88, C: 11 },
{ A: 10, B: 98, C: 65 },
{ A: 11, B: 15, C: 18 },
{ A: 16, B: 55, C: 16 },
{ A: 22, B: 23, C: 13 },
{ A: 89, B: 31, C: 13 }
];
var sum = arr.reduce((p, c) => {
var result = Object.create(null);
Object.keys(p).forEach(k => result[k] = p[k] + c[k]);
return result;
});
var sorted = Object.keys(sum).map(k => [k, sum[k]]).sort((a, b) => a[1] - b[1]);
console.log(sorted);

Related

Combining the content of multiple objects that have the same key with that key as the property in JavaScript

I want to combine the content of multiple objects that have the same key with that key as the property in JavaScript.
Objects:
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } };
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } };
Expected output:
{
med1: { a: 10, b: 12, e: 18, f: 20 },
med2: { c: 14, d: 16, g: 22, h: 24 }
}
I have tried to use both object spreading and Object.assign with no sucess.
With object spreading, since objects have no iterator this is returning an error. Object.assign would work however since the two objects to combine have the same key, the second object is overwriting the first.
You can iterate over the object keys and create a new object out of their aggregation.
You can use ES6 spread operator (...) which allows us to quickly copy all or part of an existing array or object into another array or object.
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } };
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } };
let resultObject = {};
Object.keys(cat1).map(key => { // iterate over the keys
resultObject = {
...resultObject,
[key]: {...cat1[key], ...cat2[key]} // merge two objects
}
return;
});
console.log(resultObject);
For combining two objects with a single level of indirection 👇
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } }
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } }
const merge = (o1, o2) =>
Object.keys(o1)
.reduce((p, key) =>
(p[key] = { ...o1[key], ...o2[key] }, p), {})
console.log(merge(cat1, cat2))

Edit parameter in variable with increment/decrement box and save it

I have a var K with several parameters A, B, L, M. I'm trying to put an increment/decrement box to "A" so it can be changed to a larger or smaller number.
If this is possible, it should remain changed (saved?) for the next time or session it is loaded.
{ L: 34, M: 56, A: 64, B: 65 },
{ L: 35, M: 55, A: 47, B: 89 },
{ L: 36, M: 54, A: 85, B: 62 },
{ L: 37, M: 53, A: 23, B: 11 }, ];
Since the question is tagged with reactjs, I assume this array of objects being in a component state, so we have to do it without mutating the original array.
Safest way to do it is by using a map() method from Array.prototype.
Example:
const K = [{ L: 34, M: 56, A: 64, B: 65 }, { L: 35, M: 55, A: 47, B: 89 }];
const newArray = K.map(element => ({...element, A: element.A + 1}));
console.log(newArray);
//[{ L: 34, M: 56, A: 65, B: 65 }, { L: 35, M: 55, A: 48, B: 89 }]
And now explanation. Map is a method that takes a function as an argument, where first parameter is actual value in each index of an array and returns a new value in it's place.
What's done here in ({...element, A: element.A + 1}), is that the object got populated with the properties from "element", then property A got overwritten with value of element.A + 1. After that it got returned as a new value. It happened the same for every single key.
The end result is a new array with each A increased by 1 and assigned to newArray.
And if it has to stay in a session, just keep it in a sessionStorage.
Useful links:
https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Merge objects from two different type arrays

imagine that, we have two arrays. Each of the containing objects of different type. For example:
let first: Foo[] = [
{ a: 12, b: 'x', c: 0 },
{ a: 43, b: 'y', c: 0 }
];
let second: number[] = [11, 15];
I would like merge theirs objects in a way that I finally get one array looks like below:
let first: Foo[] = [
{ a: 12, b: 'x', c: 11 },
{ a: 43, b: 'y', c: 15 }
];
As you can see I just want to assign value from the second array to c property of object from first array.
I hope that you understand my explanation of problem. I believe in your skills, guys!
you could zip the two arrays into one,
const first: Foo[] = [
{ a: 12, b: 'x', c: 0 },
{ a: 43, b: 'y', c: 0 }
];
const second: number[] = [11, 15];
const result: Foo[] = first.map((e, i) => {
return <Foo>Object.assign({}, e, { c: second[i] });
});
As so often, Array.prototype.reduce provides a good base for an approach like e.g. this one ...
var listOfItems = [
{ a: 12, b: 'x', c: 0 },
{ a: 43, b: 'y', c: 0 }
];
var listOfValues = [11, 15];
function reassignValueToGivenKey(collector, item, idx) {
item = Object.assign({}, item); // do not mutate original reference.
item[collector.key] = collector.valueList[idx]; // reassign value.
collector.itemList.push(item); // collect processed items separately.
return collector;
}
var result = listOfItems.reduce(reassignValueToGivenKey, {
key: 'c',
valueList: listOfValues,
itemList: []
}).itemList;
console.log('listOfItems : ', listOfItems);
console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
I think you should do it like this...
Maybe not the best, but should work in you case :)
This is very simple...
for(var i in second){
var elem = second[i];
first[i]['c'] = elem;
}

Compound index with lodash/underscore

When working with data from database, we often get arrays of stuff that, due to database constraints, can be (uniquely) indexed by compound indices. However, indexBy does not seem to work for compound indices, or does it?
Given an array x with objects that have properties a and b, I want to have a dictionary of dictionaries that contain all objects of x, indexed by a and b, respectively. For example:
Fiddle here.
var x = [
{
a: 1,
b: 11,
c: 101
},
{
a: 2,
b: 11,
c: 101
},
{
a: 1,
b: 11,
c: 102
},
{
a: 1,
b: 14,
c: 102
},
];
// index x by a, then by b, then by c
var byABC = _.compoundIndexBy(x, ['a', 'b', 'c']);
// there are two items in `x` with a = 1 and b = 11
console.assert(_.size(byABC[1][11]) == 2, 'Something went wrong...');
// display result
console.log(byABC);
byABC now looks like this:
{
1: {
11: {
101: {
a: 1,
b: 11,
c: 101
},
102: {
a: 1,
b: 11,
c: 102
}
},
14: {
102: {
a: 1,
b: 14,
c: 102
}
},
}
2: {
11:{
101: {
a: 2,
b: 11,
c: 101
}
}
}
}
This Fiddle demonstrates the compoundexIndexBy function. Is my work in vain (because Lo-Dash actually does support compound indices), or can it at least be improved?
You can create a mixin that recursively groups/indexes your objects:
_.mixin({
compoundIndexBy: function(lst, iteratees, context) {
if (iteratees.length === 1)
return _.indexBy(lst, iteratees[0], context);
var grouped = _.groupBy(lst, iteratees[0], context);
_.each(grouped, function(sublst, k) {
grouped[k] = _.compoundIndexBy(sublst, _.rest(iteratees), context);
});
return grouped;
}
});
console.dir(_.compoundIndexBy(x, ['a', 'b', 'c']));
If you prefer a list of objects matching the given indexes (in case of non unique paths, for example):
_.mixin({
compoundGroupBy: function(lst, iteratees, context) {
var grouped = _.groupBy(lst, iteratees[0], context);
if (iteratees.length === 1)
return grouped;
_.each(grouped, function(sublst, k) {
grouped[k] = _.compoundGroupBy(sublst, _.rest(iteratees), context);
});
return grouped;
}
});
console.dir(_.compoundGroupBy(x, ['a', 'b', 'c']));
And a demo http://jsfiddle.net/nikoshr/8w4n31vb/

Finding field.value in JSON array

var x = [{ a: 1, b: 2}, { a: 11, b: 12}, { a: 31, b: 23}, { a: 51, b: 24}]
how do you find a = 11 ?
for simple arrays one can do x.indexOf('1'); so perhaps the solution should be something like
var a1 = x.indexOf({a: 1});
ofcourse, I want to obtain the entire JSON for which the value matches.
you can do it with a simple function, no third party modules needed:
var x = [{ a: 1, b: 2}, { a: 11, b: 12}, { a: 31, b: 23}, { a: 51, b: 24}];
function getIndexOf(value){
for(var i=0; i<x.lengh; i++){
if(x[i].a == value)
return i;
}
}
alert(getIndexOf(value)); // output is: 1
You can use Array.Filter with shim support on older browsers.
var x = [{
a: 1,
b: 2
}, {
a: 11,
b: 12
}, {
a: 31,
b: 23
}, {
a: 51,
b: 24
}],
tocomp = 11;
var res = x.filter(function (ob) {
return ob.a === tocomp;
});
Result will be array of object that matches the condition.
Fiddle
And if you just care for single match and get back the matched object just use a for loop.
var x = [{
a: 1,
b: 2
}, {
a: 11,
b: 12
}, {
a: 31,
b: 23
}, {
a: 51,
b: 24
}],
tocomp = 11, i, match;
for (i=0, l=x.length; i<l; i++){
if(x[i].a === tocomp){
match = x[i];
break; //break after finding the match
}
}
Simply iterate over the array to get the value.
for(var i = 0;i < x.length; i++){
alert(x[i].a);
}
JsFiddle
You can use native js or you can use underscoreJS lib.
UnderscoreJS

Categories