Reputation: 1321
Say, I have the following array:
[
{a:1, b:"apples"},
{a:3, b:"apples"},
{a:4, b:"apples"},
{a:1, b:"bananas"},
{a:3, b:"bananas"},
{a:5, b:"bananas"},
{a:6, b:"bananas"},
{a:3, b:"oranges"},
{a:5, b:"oranges"},
{a:6, b:"oranges"},
{a:10, b:"oranges"}
]
I want to efficiëntly get for each type of 'b' the whole object with the highest a, so my function should produce:
[
{a:4, b:"apples"},
{a:6, b:"bananas"},
{a:10, b:"oranges"}
]
Now I would do something like this:
var cache = {};
var resultobj = {};
result = [];
array.forEach(function (r) {
if (cache[r.b] && cache[r.b] > r.a) {
result[r.b] = r;
}
})
for (var key in result) {
result.push(result[key]);
}
That looks terrible inefficiënt...?
Upvotes: 1
Views: 399
Reputation: 214969
It's a two-liner in ES5 and an one-liner is ES6:
ary = [
{a: 0, b:"apples"},
{a:-3, b:"apples"},
{a:-4, b:"apples"},
{a:1, b:"bananas"},
{a:3, b:"bananas"},
{a:5, b:"bananas"},
{a:6, b:"bananas"},
{a:3, b:"oranges"},
{a:5, b:"oranges"},
{a:6, b:"oranges"},
{a:10, b:"oranges"}
]
// ES5
maxes = {};
ary.forEach(function(e) {
maxes[e.b] = e.b in maxes ? Math.max(maxes[e.b], e.a) : e.a;
});
document.write('<pre>'+JSON.stringify(maxes,0,3));
// ES6
maxes = ary.reduce((m, e) =>
Object.assign(m, { [e.b]: e.b in m ? Math.max(m[e.b], e.a) : e.a }), {});
document.write('<pre>'+JSON.stringify(maxes,0,3));
Upvotes: 2
Reputation: 8926
You are close, you have some little wrong logic in the code, you have declared the cache
, but don't have used it.
if (cache[r.b] && cache[r.b] > r.a) //This always will be "false"
See working example
var array = [ { a: 1, b: "apples" }, { a: 3, b: "apples" }, { a: 4, b: "apples" }, { a: 1, b: "bananas" }, { a: 3, b: "bananas" }, { a: 5, b: "bananas" }, { a: 6, b: "bananas" }, { a: 3, b: "oranges" }, { a: 5, b: "oranges" }, { a: 6, b: "oranges" }, { a: 10, b: "oranges" } ];
var cache = {};
array.forEach(function(e) {
var t = cache[e.b];
if (t) {
t.a = t.a > e.a ? t.a : e.a;
} else {
cache[e.b] = e;
}
});
var res = Object.keys(cache).map(e => cache[e]);
document.write(JSON.stringify(res));
Upvotes: 1
Reputation: 92854
Short solution with Array.foreach
and Math.max
methods:
var map = {},result = [];
obj.forEach(function(v){
(!(v['b'] in map)) ? map[v['b']] = [v['a']] : map[v['b']].push(v['a']);
});
for (var prop in map) {
result.push({a: Math.max.apply(null, map[prop]), b: prop});
}
console.log(result);
// the output:
[
{a:4, b:"apples"},
{a:6, b:"bananas"},
{a:10, b:"oranges"}
]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
Upvotes: 1
Reputation: 24915
You can try something like this:
b
).var data = [
{a:1, b:"apples"},
{a:3, b:"apples"},
{a:4, b:"apples"},
{a:1, b:"bananas"},
{a:3, b:"bananas"},
{a:5, b:"bananas"},
{a:6, b:"bananas"},
{a:3, b:"oranges"},
{a:5, b:"oranges"},
{a:6, b:"oranges"},
{a:10, b:"oranges"}
]
var distinctB = data.slice(0,1);
data.forEach(function(o){
if(distinctB[distinctB.length-1] && o.b !== distinctB[distinctB.length-1].b){
distinctB.push(o);
}
else{
distinctB[distinctB.length-1] = o;
}
});
document.write("<pre>" + JSON.stringify(distinctB,0,4) + "</pre>");
Upvotes: 1
Reputation: 63524
Really, if your fruit names are going to be unique the best data structure is an object rather than an array of objects.
var out = arr.reduce(function (p, c) {
var key = c.b;
p[key] = p[key] || 0;
if (c.a > p[key]) p[key] = c.a;
return p;
}, {}); // { apples: 4, bananas: 6, oranges: 10 }
Upvotes: 1
Reputation: 386604
It works with a little help from an object for the indices.
var data = [{ a: 1, b: "apples" }, { a: 3, b: "apples" }, { a: 4, b: "apples" }, { a: 1, b: "bananas" }, { a: 3, b: "bananas" }, { a: 5, b: "bananas" }, { a: 6, b: "bananas" }, { a: 3, b: "oranges" }, { a: 5, b: "oranges" }, { a: 6, b: "oranges" }, { a: 10, b: "oranges" }],
result = function (array) {
var r = [], o = {};
array.forEach(function (a) {
if (!(a.b in o)) {
o[a.b] = r.push(a) - 1;
return;
}
if (r[o[a.b]].a < a.a) {
r[o[a.b]] = a;
}
});
return r;
}(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Upvotes: 1
Reputation: 68393
You are setting the values in result result[r.b] = r;
but comparing the values in cache
by doing this if (cache[r.b] && cache[r.b] > r.a) {
You need to set the values in cache
rather than result
and then iterate cache
for your intended result.
Replace the forEach and for loop with
array.forEach(function (r) {
if (cache[r.b] && cache[r.b] > r.a) {
cache[r.b] = r.a;
}
});
for (var key in cache)
{
result.push(result[key]);
}
Upvotes: 0