【问题标题】:Sort an array to have specific items first in the array对数组进行排序以使特定项目首先出现在数组中
【发布时间】:2011-08-07 16:32:27
【问题描述】:

我有一个这样的数组:

[{flag: true, other: 1},
 {flag: true, other: 2},
 {flag: false, other: 3},
 {flag: true, other: 4},
 {flag: true, other: 5},
 {flag: true, other: 6},
 {flag: false, other: 7}]

我想要这个:

[{flag: false, other: 3},
 {flag: false, other: 7},
 {flag: true, other: 1},
 {flag: true, other: 2},
 {flag: true, other: 4},
 {flag: true, other: 5},
 {flag: true, other: 6}]

基本上,我希望如果array[2].flag === false(或我选择的任何其他值)匹配元素首先放置在数组中,但在之前的匹配元素之后。 不匹配的元素保持原来的顺序。

出现的顺序很重要。

如何在 JavaScript 中做到最好?

【问题讨论】:

  • flagfalse 的元素的相对顺序如何?
  • @amn:它们在数组中“加前缀”的顺序应该是它们在原始数组中遇到的顺序。
  • @amn:问题指出不符合条件的元素保持与最初相同的顺序,我认为它也应该适用于符合条件的元素。
  • @Guffa 是的,该问题已被编辑以解决我的有效问题 :-) 但同时也添加了很多好的答案,所以我退休了......

标签: javascript arrays sorting


【解决方案1】:

ECMAScript6 中引入的Spread syntax(例如[...object])使用数组的reduce 方法使这变得相对简单:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

const sortedArr = arr.reduce((acc, element) => {
  if (!element.flag) {
    return [element, ...acc];
  }
  return [...acc, element];
}, []);

我发现这个example of extended parameter handling 真的很有帮助。

【讨论】:

  • reduce 方法不是缺少第二个参数(initialValue)吗?在这种情况下是一个空数组。这不应该吗? arr.reduce((acc, element) => {}, [])
  • 是的,你是对的。如果没有在回调函数之后提供 initialValue,reduce 将使用数组中的第一个元素作为初始值,而不是使用空数组对象。我已经更新了我的代码块以反映方法签名。
  • 这段代码看起来很棒,没有什么可添加的,但它比上面的 2 数组循环解决方案读起来好多了
  • 这是一种非常昂贵的方法。根据我的测量,const rebuiltArray = [...acc.filter(elem => elem.flag), ...acc.filter(elem => !elem.flag)] (see this answer for more details) 更易于阅读,并且证明比您的方法快 20 倍
【解决方案2】:

编写自定义排序函数,并使用flag增加优先级:

array.sort(function(a,b) {
  if (!a['flag'] && b['flag'])
    return 1;
  if (a['flag'] && !b['flag'])
    return -1;
  return a['other'] - b['other']
});

基本上,我假设列表中设置了标志的条目优先于没有标志的项目。因此,如果 a 没有标志而 b 有,则返回 1(选择 b)。如果 a 存在且 b 不返回 a。

如果两者都设置了标志或两者都没有设置,则 cmp 正常。

【讨论】:

  • 嘿,你能解释一下它是如何工作的,因为它很难在浏览器的控制台上分析。
  • 好的,我已经添加了评论。
  • 简单易懂,也很有效。感谢您与解释分享算法。
【解决方案3】:

一种更简单、更优雅的方法是通过两次过滤旧数组来构建一个新数组:一次由flag: true 过滤,一次由flag: false 过滤。总而言之,它看起来像:

// THE INITIAL ARRAY:
const originalArray = [
    {flag: true, other: 1},
    {flag: true, other: 2},
    {flag: false, other: 3},
    {flag: true, other: 4},
    {flag: true, other: 5},
    {flag: true, other: 6},
    {flag: false, other: 7}
];

// THE SORTED ARRAY:
const sortedArray = [
    ...originalArray.filter(({flag}) => flag),
    ...originalArray.filter(({flag}) => !flag)
];

在我看来,人类阅读比使用 comparerreducer 函数更容易,并且(基于测量)它的性能也相当不错解决方案。


性能比较:

我看到使用 arr.reduce()the most upvoted answer 的执行速度比此解决方案慢 20 倍左右。我用ES6 Console做对比,你也可以自己测试一下!

测量array.reduce()方式:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

// Lets multiple the array items 11 times to increase the looping process:
new Array(11).fill().forEach(x => arr.push(...arr));

console.time();

const reducedArr = arr.reduce((acc, element) => {
  if (element.flag === false) {
    return [element, ...acc];
  }
  return [...acc, element];
}, []);

console.timeEnd(); // RESULTS: between 285-350ms

测量array.filter()方式:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

// Lets multiple the array items 11 times to increase the looping process:
new Array(11).fill().forEach(x => arr.push(...arr));

console.time();

const rebuiltArray = [
  ...arr.filter(x => x.flag),
  ...arr.filter(x => !x.flag)
];

console.timeEnd(); // RESULTS: between 6-20ms

【讨论】:

  • 嗨诺伯特,谢谢!请问您为什么要将带有!! 的真/假标志转换为布尔值?
  • @Rogier 不错,实际上完全没有必要!我更新了我的答案:)
【解决方案4】:

这实际上不是排序。您可以循环遍历数组两次并构建一个新数组:

var result = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i].flag === false) {
    result.push(arr[i]);
  }
}
for (var i = 0; i < arr.length; i++) {
  if (!arr[i].flag === false) {
    result.push(arr[i]);
  }
}

您也可以使用两个结果数组和一个循环来完成,并将结果连接起来:

var result1 = [], result2 = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i].flag === false) {
    result1.push(arr[i]);
  } else {
    result2.push(arr[i]);
  }
}
var result = result1.concat(result2);

【讨论】:

    【解决方案5】:

    我认为这有点简单。 因为 javascript 将 true 视为 1 而 false 视为 0,所以您可以使用它们来创建这样的比较器,

    var comp = function(a,b){
        return a.flag*1 - b.flag*1;
    }
    

    然后你可以使用这个比较器对数组进行排序

    var arr = [{flag: true, other: 1},
             {flag: true, other: 2},
             {flag: false, other: 3},
             {flag: true, other: 4},
             {flag: true, other: 5},
             {flag: true, other: 6},
             {flag: false, other: 7}];
    arr.sort(comp);
    

    【讨论】:

      【解决方案6】:

      您可以使用值的增量进行排序。

      1. flag 排序。布尔值转换为1,如果是true,或者0,如果是false.
      2. other排序。

      var array = [{flag: true, other: 1 }, { flag: true, other: 2 }, { flag: false, other: 3 }, { flag: true, other: 4 }, { flag: true, other: 5 }, { flag: true, other: 6 }, { flag: false, other: 7 }];
      
      array.sort((a, b) =>
          a.flag - b.flag ||
          a.other - b.other
      );
      
      console.log(array);
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      【讨论】:

        【解决方案7】:

        sort 方法可以采用可选的 sortFunction 作为参数,您可以使用它来定义排序后元素的顺序

        http://www.w3schools.com/jsref/jsref_sort.asp

        【讨论】:

          【解决方案8】:

          由于您想选择某些数组项但保持它们的原始顺序,因此您需要filter() 方法。该函数是一个extension to the standard,因此您可能需要使用该页面提供的 shim,具体取决于您支持的浏览器。

          (当我第一次阅读您的问题时,我以为您想根据标志进行排序,然后根据值进行排序;为此,您可以使用内置的sort() 方法。)

          var input = [{flag: true, other: 1},
                       {flag: true, other: 2},
                       {flag: false, other: 3},
                       {flag: true, other: 4},
                       {flag: true, other: 5},
                       {flag: true, other: 6},
                       {flag: false, other: 7}]
          var false_items = input.filter(
                  function(val, idx, arr) {
                      return (val["flag"] == false);
                  });
          var true_items = input.filter(
                  function(val, idx, arr) {
                      return (val["flag"] == true);
                  });
          
          var sorted = false_items.concat(true_items);
          

          【讨论】:

            【解决方案9】:

            一种解决方案是先根据flag 对数组进行排序,然后再根据other 对数组进行排序

            var a = [{flag: true, other: 1},
             {flag: true, other: 2},
             {flag: false, other: 3},
             {flag: true, other: 4},
             {flag: true, other: 5},
             {flag: true, other: 6},
             {flag: false, other: 7}];
            
            a.sort((a, b)=> a.flag - b.flag).sort((a,b) => a.other - b.other);

            【讨论】:

              【解决方案10】:
              arr = arr.sort(function(a, b) { // sort the array using a custom function,
                                              // which is called a number of times to
                                              // determine the new order. a and b are two
                                              // elements to compare. JavaScript uses the
                                              // return value to determine the new order:
                                              // 1  - a should come later than b
                                              // -1 - a should come earlier than b
                                              // 0  - they should stay in their current order
                  return a.flag === true && b.flag === false
                          ? 1 // if a is true and b is false a should come later on
                          : a.flag === false && b.flag === true
                             ? -1 // if a is false and b is true a should come earlier
                             : a.other > b.other
                                ? 1 // if a.other > b.other, a must come later on
                                : a.other < b.other
                                  ? -1 // if a.other < b.other, a must come earlier
                                  : 0; // otherwise they are equal, so they have no order difference
              });
              

              【讨论】:

                猜你喜欢
                • 2011-02-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-07-21
                • 1970-01-01
                • 2018-03-16
                • 2018-02-27
                相关资源
                最近更新 更多