2

Lets suppose I have object like this:

var obj = {a : 5, b : 10, c : 15, d : 20, e : 20, f : 25};

I would like to get top 3 highest values - notice that d and e key have the same value and I need to get the keys also, so it would looks like:

Highest values:
f - 25
d - 20
e - 20

also if there are for example six values and four are identical:

var obj2 = {a:1, b:1, c:1, d:1, e:0,8, f: 0,5};

I need to show 4 highest.

Highest values:
a-1
b-1
c-1
d-1

I guess there is need to iterate over ALL object properties to get Math.max, but I also need a something to count 3 max numbers WITH their keys, and if there is more max (all the same) I need to "get them all!".

EDIT: there are great answers atm, so I guess I will not finish this code and just use given examples :)

Proo1931
  • 103
  • 1
  • 12

4 Answers4

3

This is an example implementation, with annotations to explain what is happening at each step.

function maxValues(o, n) {
  // Get object values and sort descending
  const values = Object.values(o).sort((a, b) => b - a);
  
  // Check if more values exist than number required
  if (values.length <= n) return o;
  
  // Find nth maximum value
  const maxN = values[n - 1];
  
  // Filter object to return only key/value pairs where value >= maxN
  return Object.entries(o)
    .reduce((o, [k, v]) => v >= maxN ? { ...o, [k]: v } : o, {});
}

const a = maxValues({
  a: 5, 
  b: 10, 
  c: 15, 
  d: 20, 
  e: 20, 
  f: 25
}, 3);
console.log(a);

const b = maxValues({
  a: 1, 
  b: 1, 
  c: 1, 
  d: 1, 
  e: 0.8, 
  f: 0.5
}, 3);
console.log(b);

const c = maxValues({
  a: 5, 
  b: 10,
}, 3);
console.log(c);

The callback passed to the Array.prototype.reduce function can be expanded out to the following:

return Object.entries(o)
    .reduce(function (obj, [key, value]) {
        if (v >= maxN) {
            return Object.assign(obj, {
                [key]: value
            });
        } else {
            return obj;
        }
    }, {});

Instead, I condensed it down using an Arrow Function Expression, ternary operator, and spread syntax.

The ternary operator is essentially shorthand for an if/else statement. E.g.

condition ? true : false;
// or
v >= maxN ? { ...o, [k]: v } : o;

The spread syntax allows an iterable value to be expanded in place. In this instance, it's being used to copy existing key/value pairs from one object literal to another.

const a = { first_name: 'Rob', gender: 'male' };
const b = { ...a, username: 'fubar' };

console.log(b); // { first_name: 'Rob', gender: 'male', username: 'fubar' };
fubar
  • 16,918
  • 4
  • 37
  • 43
  • can you elaborate more on "v >= maxN ? { ...o, [k]: v } : o, {}" I have used you code (and it works fine!) but I have problem with... understanding it :) I have never seen Conditional Operator that looks like that. Reduce has got "['a' : 5]" as 'o' (accumulator) and "['b' : 10]" as currentValue. What are the arguments after first loop? Damn this code is so sophisticated for me :) – Proo1931 Feb 12 '20 at 16:56
  • @Proo1931 - I've edited my answer to include more explanation around the code used and with links to the relevant MDN pages for a more indepth explanation. I hope this helps. – fubar Feb 12 '20 at 20:46
  • whoaaa excellent description. Everything is clear now (you have described EVERY question I had in mind - so I start to believe in telepathy :). I just didn't know that you can write a function for TWO arguments, and then add the THIRD one (in the reduce method). I did use debugger and check my results estimations in every step of the code evaluation and I understand everything. Thanks, I have learned a lot! – Proo1931 Feb 14 '20 at 03:30
2

Simply,

  1. Sort the object based on its values using, Object.entries
  2. Get the least value you can filter.
  3. Filter the entries and return as Object using Object.fromEntries.

function getTopValues(obj, topN)
{
    var sortedEntries = Object.entries(obj).sort(function(a,b){return b[1]-a[1]});
    var last = sortedEntries[topN-1][1];
    var result = sortedEntries.filter(function(entry){
        return entry[1] >= last;
    });
    console.log(Object.fromEntries(result));
}

getTopValues({a:5, b:10, c:15, d:20, e:20, f:25}, 3);
getTopValues({a:1, b:1, c:1, d:1, e:0.8, f: 0.5}, 3);
getTopValues({a:1, b:1, c:1, d:1, e:0.8, f: 0.5}, 5);
Vignesh Raja
  • 7,927
  • 1
  • 33
  • 42
1

So, you want to find the top 3 highest and if there are multiple identical highest then you want to include all of that.

This problem is asked in a slightly weird fashion.

I am going to assume that if there is something like a:1 b:1 c:2 d:2 e:3, you would like to include a,b,c and d.

First of all, you only have to keep track of the keys because you can get the values instantly at the end.

Ok! Let's start. (efficient but ugly)

class Numandamount {
  constructor(number, amount) {
    this.number = number;
    this.amount = amount;
  }
}
//That's just a class to link numbers and their amounts

var numtoamount = [];

//Now let's fill that array!

for (var property in obj) {
  if (obj.hasOwnProperty(property)) {
    var num = obj.property;
    var found = false;
    for(Numandamount naa in numtoamount){
       if(naa.number == num){
           naa.amount++;
           found = true;
       }
    }
    if(!found){
       naa.push(new Numandamount(num,1));
    }
  }
}

//The array is done!

numtoamount.sort(function(a,b){return b.number-a.number});

//Now all we have to do is loop through it!

var count = 0; // keep track of how many we did
var i = 0;
while(count<4 && i<numtoarray.length){
    count += numtoamount[i].amount;
    i++;
}
//BOOOM WE DID IT
// I is the biggest index so all we have to do is:

for(var j = 0;j<i;j++){
   console.log("We have "+numtoamount[j].amount+" "+numtoamount[j].number+"'s");
}

For eg. it will print out for this example obj: {a:1 b:1 c:4 d:6 e:7 f:4}

We have 1 7's We have 1 6's We have 2 4's

If you would like some other implementation please comment below! I put my heart into this <3

Danyman
  • 63
  • 1
  • 9
1

I would start with transforming your object into an array of objects:

const arr = []

for (var key in obj){
  arr.push( {[key]: obj[key]} )
}

Now you have an array that looks like this:

[
  {"f": 25},
  {"d": 20},
  {"e": 20},
  {"c": 15},
  {"b": 10},
  {"a": 5}
]

Now you can sort your objects by the magnitude of their values:

const sortedArray = arr.sort( (a,b) => { 
  if (Object.values(a)[0] > Object.values(b)[0]) {
    return -1
  } 
})

Which would give:

[
  {"f": 25},
  {"d": 20},
  {"e": 20},
  {"c": 15},
  {"b": 10},
  {"a": 5}
]

Then you can just pick however many values off the top you want. For example

sortedArray.filter( (item, index) => {
    if (index <= 2 || Object.values(item)[0] === Object.values(sortedArray[0])[0]) {
      return item
    }
  })

Which gives:

[
  {"f": 25},
  {"d": 20},
  {"e": 20}
]

Or in the case of your second object, it would match the n highest values, but also grab any other values that are equal to the highest value.

As a single function:

function sortYourObject(object, number){

  var arr = []

  for (var key in object){
    arr.push( {[key]: object[key]} )
  }

  const sortedArray = arr.sort( (a,b) => { 
    if (Object.values(a)[0] > Object.values(b)[0]) {
      return -1
    } 
  })

  const endresult = sortedArray.filter( (item, index) => {
    if (index <= 2 || Object.values(item)[0] === Object.values(sortedArray[0])[0]) {
      return item
    }
  })

  return endresult

}
Seth Lutske
  • 9,154
  • 5
  • 29
  • 78