【问题标题】:Lodash: Sort array of objects prioritizing alphabets first followed by numbersLodash:对对象数组进行排序,首先是字母,然后是数字
【发布时间】:2017-05-11 08:14:58
【问题描述】:

我有一个对象数组,比如说:

var objects = [
  {name: 'A'},
  {name: '1'},
  {name: 'B'}
]

知道我可以使用Lodash sortBy对其进行排序:

objects= _.sortBy(objects, 'name')

这将导致:

[
  {name: '1'},
  {name: 'A'},
  {name: 'B'}
]

但我想要的输出是这样的:

[
  {name: 'A'},
  {name: 'B'},
  {name: '1'}
]

请帮忙。

【问题讨论】:

  • @TrojanByAccident 是的,相同的密钥是什么意思?它们是三个不同的对象。我已经展示了我使用 Lodash soryBy 尝试过的内容,当我发布我尝试过的内容时,“付出一些努力”是什么意思?标记您的评论。
  • 可能的字符范围是多少?只有 A-Z 和 0-9?
  • @RobbyCornelissen 是的,先生。
  • @user1422866 这并没有完全显示您尝试过的内容。这就是说,您知道其他东西不会给您想要的结果,但您没有显示任何尝试实现该结果的自己的代码。
  • 在对值AAA1 进行排序时,您希望哪个排在第一位?即,您的排序规则仅适用于第一个字符还是也适用于后续字符?

标签: javascript arrays string sorting lodash


【解决方案1】:

使用Array#sort 你可以应用这个逻辑:

// If both are numbers or both are not numbers
isNaN(a.name) === isNaN(b.name) ?
     // then compare between them 
    a.name.localeCompare(b.name)
    : // else
    // If the 1st is not a number move it up, if it's a number move it down
    (isNaN(a.name) ? -1 : 1); 

没有lodash:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

objects.sort(function(a, b) {
  return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
});

console.log(objects);

作为 lodash 链的一部分:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

var result = _(objects)
  .sort(function(a, b) {
    return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
  }) 
  .value();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.3/lodash.min.js"></script>

【讨论】:

  • 如果两项相同,排序函数必须返回 0。否则结果是不可预测的。此外,isNaN"" 有奇怪的行为...isNaN("") == isNaN("0") 是真的。
  • @SalmanA - 切换到String@localeCompare,它同时处理了这两个问题。
【解决方案2】:

我不确定使用 lodash 的 sortBy 是否是解决此问题的正确方法。这是使用 Javascript 的 Array#sort 方法的实现。

排序时不仅要考虑第一个字符,还要考虑整个字符串。

const objects = [{
  name: '2'
}, {
  name: 'B'
}, {
  name: '1'
}, {
  name: 'A'
}, {
  name: 'A1'
}, {
  name: 'AA'
}]

objects.sort((o1, o2) => {
  let a = o1.name, b = o2.name;
  let isDigit = x => x >= 48 && x <= 57;

  for (let i = 0, n = Math.min(a.length, b.length); i < n; i++) {
    let aCharCode = a.charCodeAt(i), bCharCode = b.charCodeAt(i);
    
    if (aCharCode !== bCharCode) {
      if (isDigit(aCharCode) && !isDigit(bCharCode)) return 1;
      if (isDigit(bCharCode) && !isDigit(aCharCode)) return -1;
      return aCharCode - bCharCode;
    }
  }

  return a.length - b.length;
});

console.log(objects)

对于给定的输入,打印出来

[
  {
    "name": "A"
  },
  {
    "name": "AA"
  },
  {
    "name": "A1"
  },
  {
    "name": "B"
  },
  {
    "name": "1"
  },
  {
    "name": "2"
  }
]

【讨论】:

    【解决方案3】:

    解决方案有两个_.sortBy()

    一个用于优先排序字母,然后另一个用于对元素进行排序。

    在我看来,它更具可读性并且不会对性能产生影响。

    const objects = [
      {name: 'B'},
      {name: '2'},
      {name: '1'},
      {name: 'A'}
    ]
    
    const result = _.sortBy(_.sortBy(objects, o => !isNaN(parseInt(o.name)), 'name'))
    
    console.log(result)
    &lt;script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"&gt;&lt;/script&gt;

    【讨论】:

    • 这个也只考虑了字符串的第一个字符。在 cmets 中,OP 声明字符串最多可以包含 20 个字符。
    【解决方案4】:

    对输入进行分区,分别排序并返回:

    代码:

    const _ = require('lodash');
    
    const customSort = (a) => _.chain(_.partition(a, i => isNaN(i.name))).flatMap(p => _.sortBy(p, 'name')).value();
    
    const input = [
        { name: '1' },
        { name: 'A' },
        { name: '6' },
        { name: 'B' },
        { name: 'a' },
        { name: '0' },
        { name: '3' }];
    
    console.log(customSort(input));
    

    输出:

    [ { name: 'A' },
      { name: 'B' },
      { name: 'a' },
      { name: '0' },
      { name: '1' },
      { name: '3' },
      { name: '6' } ]
    

    【讨论】:

      【解决方案5】:

      如果name 是一个整数前缀,则使用z 进行比较。

      var objects = [
        {name: 'z'},
        {name: 'A'},
        {name: '1'},
        {name: 'B'}
      ], sorted = _.sortBy( objects, [
              function(d) {
                  return !isNaN(parseFloat(d.name))
                         && isFinite(d.name)
                             ? 'z' + d.name
                             : d.name;
              }
          ]
      );
      console.log(sorted);
      &lt;script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"&gt;&lt;/script&gt;

      【讨论】:

      • 很好,但它只考虑字符串的第一个字符来进行比较。
      • 这将按照 OP 中描述的要求工作。也就是说,首先对字母数字names 进行排序,然后再按字母排序;升序。字符串A1 被认为是一个单词(字母数字),应该这样排序。如果这不是最初的意图,OP'er 应该扩展需求。
      • 我的解释显然与您的不同,因此澄清确实会有所帮助。为问题添加了评论。
      猜你喜欢
      • 2021-12-28
      • 2023-03-26
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      • 2013-06-29
      • 2011-01-15
      • 2015-01-31
      • 2017-03-31
      相关资源
      最近更新 更多