【问题标题】:JavaScript sorting array by nullable boolean, then stringJavaScript 按可空布尔值排序数组,然后是字符串
【发布时间】:2019-12-22 17:18:19
【问题描述】:

我正在尝试对具有可为空的布尔值和标题的对象数组进行排序。如果列表中的某个项目已设置为重要,则"ReadUnderstood"truefalse,如果不是,则为null。如果"ReadUnderstood"truenull,我希望列表按字母顺序排列,但如果值为false,我希望它位于列表顶部。

下面的代码最接近我想要的。这将按字母顺序返回列表,其中"ReadUnderstood"false 的项目位于列表顶部。但是"ReadUnderstood"true 的项目最终出现在列表的末尾,而不是按字母顺序排列。任何帮助将不胜感激。

items = [
    {Title: 'A', ReadUnderstood: null},
    {Title: 'C', ReadUnderstood: false},
    {Title: 'E', ReadUnderstood: null},
    {Title: 'B', ReadUnderstood: true},
    {Title: 'D', ReadUnderstood: true},
    {Title: 'F', ReadUnderstood: null},
]

items.sort((a, b) => { 
    return (b.ReadUnderstood != null && b.ReadUnderstood == false) - (a.ReadUnderstood != null && a.ReadUnderstood == false) || a.Title - b.Title; 
})

Desired result:
items = [
    {Title: 'C', ReadUnderstood: false},
    {Title: 'A', ReadUnderstood: null},
    {Title: 'B', ReadUnderstood: true},
    {Title: 'D', ReadUnderstood: true},
    {Title: 'E', ReadUnderstood: null},
    {Title: 'F', ReadUnderstood: null},
]

【问题讨论】:

  • 请添加输入样本和预期输出以创建minimal reproducible example
  • 多个false 值怎么样。它们也需要按字母顺序排序吗?
  • 那也更好,是的:)

标签: javascript arrays sorting object


【解决方案1】:

你不能减去字符串来比较它们(减去非数字字符串总是会得到NaN)。
当涉及的操作数是数字(数字、布尔值等)时,您只能使用- 进行排序,否则您将得到错误的排序顺序。

要比较字符串,您可以使用><

if(a.Text < b.Text)
  return -1;
else if(a.Text > b.Text)
  return 1;
else
  return 0;

或者使用String.prototype.localeCompare方法:

return a.Text.localeCompare(b.Text);

localeCompare() 还具有正确排序 unicode 字符(例如 é)的优势。

您的项目的完整工作示例:

let items = [
  {Title: 'A', ReadUnderstood: null},
  {Title: 'C', ReadUnderstood: false},
  {Title: 'E', ReadUnderstood: null},
  {Title: 'B', ReadUnderstood: true},
  {Title: 'D', ReadUnderstood: true},
  {Title: 'F', ReadUnderstood: null},
];


items.sort((a, b) => {
  let aReadUnderstood = a.ReadUnderstood !== false;
  let bReadUnderstood = b.ReadUnderstood !== false;
  if(aReadUnderstood !== bReadUnderstood)
    return aReadUnderstood - bReadUnderstood;
  else
    return a.Title.localeCompare(b.Title); 
});

console.log(items);

使用像 lodash 这样的库,你可以让它更短更简洁:

let items = [
  {Title: 'A', ReadUnderstood: null},
  {Title: 'C', ReadUnderstood: false},
  {Title: 'E', ReadUnderstood: null},
  {Title: 'B', ReadUnderstood: true},
  {Title: 'D', ReadUnderstood: true},
  {Title: 'F', ReadUnderstood: null},
];

items = _.sortBy(items, e => [e.ReadUnderstood !== false, e.Title]);
console.log(items);
&lt;script src="https://unpkg.com/lodash@4.17.15/lodash.min.js"&gt;&lt;/script&gt;

【讨论】:

    【解决方案2】:

    这是另一种选择。首先根据item.ReadUnderstood === false对数组进行分区。然后对两个数组进行排序并将它们连接在一起。

    Array.prototype.partition = function (callback, thisArg) {
      if (thisArg !== undefined) callback = callback.bind(thisArg);
      
      const partitions = { true: [], false: [] };
      this.forEach((item, ...args) => partitions[!!callback(item, ...args)].push(item));
      return [partitions[true], partitions[false]];
    };
    
    const items = [
      {Title: 'A', ReadUnderstood: null},
      {Title: 'C', ReadUnderstood: false},
      {Title: 'E', ReadUnderstood: null},
      {Title: 'B', ReadUnderstood: true},
      {Title: 'D', ReadUnderstood: true},
      {Title: 'F', ReadUnderstood: null},
    ];
    
    const [ruFalse, ruOther] = items.partition(item => item.ReadUnderstood === false);
    const byTitle = ({Title: t1}, {Title: t2}) => t1.localeCompare(t2);
    console.log(ruFalse.sort(byTitle).concat(ruOther.sort(byTitle)));

    如果您不关心 ruFalse 是否被排序,您可以在该列表中省略 .sort(byTitle)

    【讨论】:

      【解决方案3】:

      试试

      items.sort((a,b) => a.ReadUnderstood===false ? 
        -1 : (b.ReadUnderstood===false ? 1 : a.Title.localeCompare(b.Title)));
      

      items = [
          {Title: 'A', ReadUnderstood: null},
          {Title: 'C', ReadUnderstood: false},
          {Title: 'E', ReadUnderstood: null},
          {Title: 'B', ReadUnderstood: true},
          {Title: 'D', ReadUnderstood: true},
          {Title: 'F', ReadUnderstood: null},
      ]
      
      items.sort((a,b) => a.ReadUnderstood===false ? 
            -1 : (b.ReadUnderstood===false ? 1 : a.Title.localeCompare(b.Title)));
            
      console.log(items);

      【讨论】:

      • 您需要比较两个对象之间的ReadUnderstood,否则排序将无法正常工作。当 C 项是数组中的第一项时,您的排序会将其排序在中间的某个位置而不是顶部
      • 这是一个失败的例子:es6console.com/jze1oz4o
      • 几乎,你还需要处理两者都是false的情况,然后它们需要按字母顺序排序。例如在此示例中,C 应位于 G 之前:es6console.com/jze23c0m
      【解决方案4】:

      你很接近。 compareFunction 应该返回一个数字,并根据它是正数、负数还是零,被比较的两个项目 (a, b) 相对于彼此移动。减去布尔值会返回一个数字。所以,第一个条件工作正常。对于字符串,您需要使用localeCompare 按字母顺序对其进行排序。

      此外,您可以简化第一个条件。您无需检查null 并为false 进行严格的相等检查。

      items.sort((a, b) => 
        (b.ReadUnderstood === false) - (a.ReadUnderstood === false) 
          || a.Title.localeCompare(b.Title)
      )
      

      这是一个有效的 sn-p:

      const items = [
          {Title: 'A', ReadUnderstood: null},
          {Title: 'C', ReadUnderstood: false},
          {Title: 'E', ReadUnderstood: null},
          {Title: 'B', ReadUnderstood: true},
          {Title: 'D', ReadUnderstood: true},
          {Title: 'F', ReadUnderstood: null},
      ]
      
      items.sort((a, b) => 
        (b.ReadUnderstood === false) - (a.ReadUnderstood === false) 
          || a.Title.localeCompare(b.Title)
      )
      
      console.log(items)

      【讨论】:

      • 很奇怪。您的 sn-p 正在做我想做的事,但是当我尝试在我的代码中实现它时,列表仅按字母顺序排序
      • @Hammis82 我在箭头函数中使用了隐式返回。由于您的 compareFunction 中有 {},因此您需要添加 return
      • 我不知道一个布尔值可以减去/添加到另一个布尔值。每天学习一些东西。对于其他人阅读:true 代表 1false 代表 0true - false //=&gt; 1
      • @3limin4t0r 扩展 "true 代表 1":您可以在 javascript 中减去任何内容。它们被强制转换为基于操作数的不同类型。在arithmetic context 中,ToNumber 在 2 个操作数上被调用。从链接表中可以看出,“参数为真则结果为1。参数为假时结果为+0”.
      • @3limin4t0r 对于对象,ToNumber 调用,valueOf 方法。因此,如果您覆盖对象的valueOf 函数,则可以在算术运算中使用它们。 var a = { valueOf: () =&gt; 1 }; var b = { valueOf: () =&gt; 2 } 所以,b - a 返回 1
      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 2021-09-30
      • 2013-03-31
      • 2017-11-06
      • 1970-01-01
      • 2021-12-20
      • 2016-12-24
      • 1970-01-01
      相关资源
      最近更新 更多