Top highest values in an object (more if there are more max values and they are the same)

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 :)

Answers:

Answer

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' };
Answer

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);

Answer

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

Answer

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

}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.