简体   繁体   中英

Sort collections by ranking them in multiple criteria

So this is the task I'm trying to solve.

We have one master array and few minions arrays like this:

var master = [3, 2, 2, 4, 15, 8];
var minions = [
  {
    id : "Minion1",
    imitation : [2, 1, 0, 3, 11, 5],
  },
  {
    id : "Minion2",
    imitation : [1, 3, 2, 1, 12, 7],
  },
  {
    id : "Minion3",
    imitation : [4, 3, 1, 1, 11, 6],
  }
];

Let's assume that minions try to mimic the Master. And we need to know who did it better, by giving them a points.

Restrictions: - there can be any number of minions - we comparing only numeric values - amount of numbers in arrays always stay the same


So I came up with this logic:

  1. Subtract each number in minions arrays from the corresponding number in the master and get the absolute value of this. Because if Master have first number 3, Minion1 has number 2 and Minion3 has number 4. Minion1 and Minion3 equally far away from the Master.

  2. Push absolute values to the array for each minion.

  3. Compare absolute values in arrays and sort minions for each number in arrays. Maybe something like for master[0]:

    • Minion1[0]
    • Minion3[0]
    • Minion2[0]
  4. Assign points to minions for each number in an array depending on their position, comparative to each other. Master[0]:

    • Minion1[0] +3 points
    • Minion3[0] +2 points
    • Minion2[0] +1 points
  5. Sum the points for each number in minions arrays. Compare sums and console.log the better imitator


Right now, I'm stuck on step 3. Will be grateful for any comments on my code or logic, thx :)

This is what I got so far:

 var Master = [3, 2, 2, 4, 15, 8]; var minions = [ { id : "minion1", mimic_result : [2, 1, 0, 3, 11, 5], abs_values : [], points : [] }, { id : "minion2", mimic_result : [1, 3, 2, 1, 12, 7], abs_values : [], points : [] }, { id : "minion3", mimic_result : [6, 0, 0, 0, 0, 1], abs_values : [], points : [] } ]; for(i=0;i<Master.length;i++){ for(j=0;j<minions.length;j++) { var result = Math.abs(Master[i]-minions[j].mimic_result[i]); minions[j].abs_values.push(result); } } for(k=0;k<minions.length;k++) { console.log(minions[k].result[0]); } console.log(minions[0].abs_values); console.log(minions[1].abs_values); console.log(minions[2].abs_values); 

You can treat your arrays as points in an n-dimensional space and calculate the Euclidean distance between them:

function dist(a, b) {
    return Math.sqrt(a.reduce(function(sum, _, i) { return sum + Math.pow(a[i] - b[i], 2) }, 0));
}

and then

minions.sort(function(a, b) {
    return dist(master, a.imitation) - dist(master, b.imitation);
});

Using Lo-Dash :

var minionPoints = minions.map(function (minion, i) {
    return {
        minionIndex: i,
        points: 0
    };
});
master.forEach(function (masterValue, masterIndex) {
    var distances = [],
        buckets = [],
        points = 0;
    // calculate distances from master on one individual property
    minions.forEach(function (minion, minionIndex) {
        distances.push({
            minionIndex: minionIndex,
            distance: Math.abs(masterValue - minion.imitation[masterIndex])
        });
    });
    // divide into buckets containing items with same distance
    // (ordered from largest distance to smallest)
    _.sortBy(distances, 'distance').forEach(function (o) {
        if (buckets[0] && (o.distance === buckets[0][0].distance)) {
            buckets[0].push(o);
        } else {
            buckets.unshift([o]);
        }
    });
    // calculate point gain for each item in the bucket
    // (if there are 2 items in the last and second-last place, they should each get 1.5 points)
    buckets.forEach(function (bucket) {
        var totalGain = points + bucket.length + ((bucket.length * (bucket.length + 1)) / 2)
            individualGain = totalGain / bucket.length;
        bucket.forEach(function (o) {
            minionPoints[o.minionIndex].points += individualGain;
        });
        points += totalGain;
    });
});
_.sortBy(minionPoints, 'points').reverse().forEach(function (o, i) {
    console.log((i + 1) + '. ' + minions[o.minionIndex].id + ' (' + o.points +
            ' points, imitation [' + minions[o.minionIndex].imitation.join(', ') +
            '])');
});

It calculates the distance and points as you suggested. In addition, when multiple minions have the same distance, their total point gain is divided equally between them. Working demo.

(I included Lo-Dash automatically, out of laziness. I used it ultimately only for sorting, which could be just as easily done using the native Array.prototype.sort .)

First of all, an exception ts raised in :

for(k=0;k<minions.length;k++) {
  console.log(minions[k].result[0]);
}

Should be:

for(k=0;k<minions.length;k++) {
  console.log(minions[k].mimic_result[0]);
}

Or from what I understand you want to check the abs_values values you have to do:

for(k=0;k<minions.length;k++) {
  console.log(" the minions["+k+"] abs_values are:\n"); 
  for(l=0;l<minions.abs_values.length;l++) {
    console.log(minions[k].abs_values[l]);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM