【问题标题】:Return sorted unique values from array, but if count is equal, return values in order从数组中返回排序后的唯一值,但如果计数相等,则按顺序返回值
【发布时间】:2017-07-19 19:51:38
【问题描述】:

我正在创建一个函数,它接收一个未排序的整数数组并返回一个按频率排序的唯一整数数组。但是,如果整数具有相同的频率,它们将按照输入数组的原始顺序返回。这是我当前的功能:

function uniqueUnionSorted(arr) {
  counter = {};
  for(var i=0; i<arr.length; i++) {
    if (arr[i] in counter) {
      counter[arr[i]] ++;
    } else {
      counter[arr[i]] = 1;
    }
  }
  sortedStrings = Object.keys(counter).sort(function(a,b) {
    return counter[b] - counter[a]
  });
  var sortedNumbers = sortedStrings.map(Number);
  return sortedNumbers;
}

所以对于这样的数组:

arr = [1, 3, 2, 1, 5, 2, 1, 4]

函数应该返回:

[1,2,3,5,4]

但是,我的函数正在对 5 和 4 进行排序并返回:

[1,2,3,4,5]

请帮忙!

【问题讨论】:

    标签: javascript arrays sorting unique


    【解决方案1】:

    这种重新排序的原因是数字对象属性在使用Object.keys() 时会按顺序排列。

    不要将counter定义为对象,而是使用Map,它将保留插入顺序:

    function uniqueUnionSorted(arr) {
      var counter = new Map();
      for(var i=0; i<arr.length; i++) {
        counter.set(arr[i], (counter.get(arr[i]) || 0) + 1);
      }
      // Spreading the Map will produce an array of pairs
      var sortedNumbers = [...counter].sort(function(a,b) {
        return b[1] - a[1]; // sort by count
      }).map(a => a[0]); // only keep the values, not the counts
      return sortedNumbers; // Map keys retain original type, so they remain numeric
    }
    
    arr = [1, 3, 2, 1, 5, 2, 1, 4]
    
    console.log(uniqueUnionSorted(arr));

    【讨论】:

      【解决方案2】:

      在计数器对象中,我们还可以保存每个元素的最低索引,这样我们就可以在排序数组中保留较低索引的元素。

      function uniqueUnionSorted(arr) {
        counter = {};
        for(var i=0; i<arr.length; i++) {
      if (arr[i] in counter) {
        counter[arr[i]].count ++;
      } else {
        counter[arr[i]] = {'count' : 1, 'index' : i}; //save lowest index of element
      }
        }
        sortedStrings = Object.keys(counter).sort(function(a,b) {
          return counter[b].count - counter[a].count || counter[a].index - counter[b].index;
        });
        var sortedNumbers = sortedStrings.map(Number);
        return sortedNumbers;
      }
      
      console.log(uniqueUnionSorted([1, 3, 2, 1, 5, 2, 1, 4]));

      【讨论】:

      • 很好的解决方案。您可以将排序回调返回表达式缩短为:return counter[b].count - counter[a].count || counter[a].index - counter[b].index;
      【解决方案3】:

      https://jsfiddle.net/anLrwwfa/4/

      这是另一种方法,您可以使用 Set 对象、存储频率的对象和保持原点不变的原始数组来执行此操作。它比当前流行的答案要长一点,但我正在写它,所以我想我会把我的帽子扔进戒指。

      function sortArr(arr) {
        let uniqueValues = new Set();
        let frequencies = new Object();
      //add values to set object
        arr.map((val) => uniqueValues.add(val));
      
      //get frequencies of values
        for (let val of uniqueValues) {
          frequencies[val] = 0;
        }
        arr.map((val) => frequencies[val]++);
      
      //sort by frequency, then sort by original placement in array
       let sorted_arr = Array.from(uniqueValues).sort((a, b) => {
          return frequencies[a] - frequencies[b];
        }).sort((a, b) => {
            return (frequencies[a] === frequencies[b]) ?
              Array.from(uniqueValues).indexOf(a) - Array.from(uniqueValues).indexOf(b) :
              b;
          });
      //return array
          return sorted_arr;
        };
      
      sortArr([1, 3, 2, 1, 5, 2, 1, 4]);  //1,2,3,5,4
      

      编辑 稍微优化一下代码

      【讨论】:

        【解决方案4】:

        如果频率相等,可能会在原始数组索引之后添加一个附加条件来实际排序:

        sortedStrings = Object.keys(counter).sort(function(a,b) {
          return counter[b] - counter[a] || arr.indexOf(+a)-arr.indexOf(+b)
         });
        

        http://jsbin.com/zewulanozo/edit?console

        【讨论】:

        • 我没有投反对票,但我要提一下,在 sort 回调中使用 indexOf 会使 sortO(n²logn) 中运行而不是 O(nlogn).
        • @trincot 仅在最坏的情况下。在上面的例子中,这发生了一次,这仍然回答了问题......
        • 确实,我同意。
        猜你喜欢
        • 2012-03-06
        • 1970-01-01
        • 2013-08-24
        • 2013-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-04
        相关资源
        最近更新 更多