Search code examples
javascriptarrayssortinglodashranking

Assign ranks to objects by values in an array of objects


I have an array of objects like so:

var data = {
    a: [
        { keyone:'c', keytwo: 'anna', keythree: 21, keyfour: 15 },
        { keyone:'a', keytwo: 'anna', keythree: 22, keyfour: 15 },
        { keyone:'s', keytwo: 'anna', keythree: 10, keyfour: 15 },
        { keyone:'v', keytwo: 'anna', keythree: 7, keyfour: 15 }
    ],

    b: [
        { keyone:'f', keytwo: 'any', keythree: 45, keyfour: 100 },
        { keyone:'b', keytwo: 'any', keythree: 146, keyfour: 100 },
        { keyone:'t', keytwo: 'any', keythree: 23, keyfour: 100 },
        { keyone:'h', keytwo: 'any', keythree: 11, keyfour: 100 }
    ]
  };

I want to assign ranks to each object, based on values of keythree and keyfour, within the groups as well as within the entire data set. How would I do it?

Update: I have depicted the ranks in my code above.

Resultant object:

var data = {
    a: [
        { keyone:'c', keytwo: 'anna', keythree: 21, keyfour: 15, rankgroup: 3, rankall: 4 },

        { keyone:'a', keytwo: 'anna', keythree: 22, keyfour: 15, rankgroup: 4, rankall: 5 },

        { keyone:'s', keytwo: 'anna', keythree: 22, keyfour: 15, rankgroup: 2, rankall: 2 },

        { keyone:'v', keytwo: 'anna', keythree: 7, keyfour: 15, rankgroup: 1, rankall: 1 }
    ],

    b: [
        { keyone:'f', keytwo: 'any', keythree: 45, keyfour: 100 },

        { keyone:'b', keytwo: 'any', keythree: 146, keyfour: 100 },

        { keyone:'t', keytwo: 'any', keythree: 23, keyfour: 100 },

        { keyone:'h', keytwo: 'any', keythree: 11, keyfour: 100 }
    ]
};

I am using lodash. My idea is to first sort the array based on those keys, then loop over the original object, insert the sorted index by comparing another key. This is what I have tried:

var keys = Object.keys(data);
var result = {};
var numkeys;
for(var i=0; i < keys.length; i++) {
    if(!numkeys) {
        var numkeys = _.keys(_.pick(data[keys[i]][0], _.isNumber));
  }

        for(var j=0;j<numkeys.length;j++) {
    var sorted = _.sortBy(data['a'], numkeys[j]);
        _.forEach(sorted, function(n, k) {

        //THIS FAILS
        var t = _.set(_.where(data[keys[i]], {keyone: n.keyone}), keys[i]+'rank', k);
        console.log(t);
        });

    }
  }

How would I do it? My logic seems too complex and the set method does not update the original object by key but adds a new entry after the main object.

Update: Notice the duplicate occurrence of 22 for the object a. This leads to an issue when assigning ranks, since indexOf will always return the index of the first occurrence, hence the second occurrence will never have an index assigned to it and hence the value will be undefined.


Solution

  • this is how I achieved it.

    1. collect all keythree into an array and sort them (to assign rankall based on index).

      var all = [];
      _.forEach(data, function (a, key) {
          _.forEach(a, function(n, k){
          all.push(n.keythree);
        });
      });
      all.sort(function(a,b){
          return a-b;
      });
      
    2. assign ranks

      _.forEach(data, function (a, key) {
          var sorted = _.sortBy(a, 'keythree');
          _.forEach(sorted, function(n, k) {
            var index = _.findIndex(data[key], {keyone: n.keyone});
            data[key][index]['rankgroup'] = k+1;
            data[key][index]['rankall'] = all.indexOf(n.keythree)+1;
          });
      });
      

    check this fiddle


    EDIT

    i'm creating another array for dupes

    _.forEach(a, function(n, k) {
        if (all.indexOf(n.keythree) !== -1) {
            dupes.push(n.keythree);
        }
        all.push(n.keythree);
    });
    

    and for getting the global rank for these dupe items

        function getGlobalRank(n) {
        var val = n.keythree;
        if (sorted_dupes[val] === undefined) {
            sorted_dupes[val] = [];
            _.forEach(data, function(a, key) {
                _.forEach(_.where(a, {
                    keythree: val
                }), function(b) {
                    sorted_dupes[val].push(b);
                });
            });
            sorted_dupes[val] = _.sortByAll(sorted_dupes[val], ['keyfour', 'keytwo', 'keyone']);
        }
        return _.findIndex(sorted_dupes[val], {
            keyone: n.keyone,
            keytwo: n.keytwo,
            keythree: n.keythree,
            keyfour: n.keyfour
        }) + 1 + all.indexOf(val);
    }
    

    see that the items are sorted based on all the properties in the order keythree, keyfour, keytwo, keyone (you can change the order inside _.sortByAll if you want to)

    the code looking uglier than i thought. will update the refactored code soon

    check the fiddle