JavaScript:按包括未定义的数字属性对对象数组进行排序

3
我可以定义一个数字数组并按照以下方式对它们进行排序。

var array = ['2', undefined, '1'];
array.sort((a, b) => a - b);
console.log(array);

输出结果为1、2、null
我有一个对象数组,其中包含一个可以定义为数字属性的属性。并非所有对象都具有数字属性。现在我想按数字属性对这个对象数组进行排序。所以我尝试了这个方法:

var array = [
  { number: 2 },
  {},
  { number: 1 }
];
array.sort((a, b) => a.number - b.number);
console.log(array);

输出结果并非我想要的。我得到了 2, undefined, 1
如果数组中所有对象都定义了 number 属性,相同的排序函数会产生 1, 2,这是我期望的结果。

var array = [
  { number: 2 },
  { number: 1 }
];
array.sort((a, b) => a.number - b.number);
console.log(array);

我的问题是,如何以最佳方式对对象数组按属性进行排序,其中该属性并非总是定义的。预期结果是按属性(数字)对对象进行排序,并将未定义该属性的对象放在数组末尾。
到目前为止,我唯一的解决方案是遍历集合,如果未定义属性,则将属性设置为0或null。然后我可以将对象数组传递给sort函数,它会按照我想要的方式工作。有比这更好的方法吗?
Jfiddle链接:https://jsfiddle.net/t2r6z13e/1/
6个回答

6
您可以返回要比较的每个对象上"number"属性的存在性之间的差异,或者返回它们的"number"属性之间的差异(在两个对象都具有该属性的情况下)。

var array = [
  { number: 2 },
  {},
  { number: 1 }
];

array.sort((a, b) => {
  const aHas = typeof a.number !== 'undefined';
  const bHas = typeof b.number !== 'undefined';
  return (bHas - aHas) || (aHas === true && a.number - b.number) || 0;
});
console.log(array);


这个问题已经有几个很好的解决方案,其中包括来自georg的非常简洁的解决方案。我接受了这个答案,因为它比georg的答案具有更广泛的浏览器兼容性。 - F_SO_K

3
您可以使用对象扩展为数组元素提供默认值:{default, ...item}

var array = [
  { number: 2 },
  {},
  { number: 1 },
  {},
  { number: 0 },
];

array.sort((a, b) => (
    {number: Number.MAX_VALUE, ...a}.number -
    {number: Number.MAX_VALUE, ...b}.number));

console.log(array);

如果你的目标环境还不支持对象扩展运算符,可以将其替换为Object.assign


那么你暂时将 Infinity 分配给数字属性,如果已经设置了另一个 number 属性,则第二个值将覆盖它? - Rob Monhemius
1
根据ECMA规范,Infinity - Infinity返回NaN。请参见https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%201st%20edition,%20June%201997.pdf的11.6.3章节。不知道这是否会导致潜在问题,但了解这一点可能是有益的;)。在`sort`方法中的常规回调函数应返回`0`而不是`NaN`。 - Rob Monhemius
这是一个非常优秀且简洁的解决方案。它的缺点是不兼容Internet Explorer。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Browser_compatibility - F_SO_K

3
解释在注释中:

let array = [
  {},
  { number: 2 },
  {},
  { number: 1 }
];

array.sort((a, b) => {
  // two undefined values should be treated as equal ( 0 )
  if( typeof a.number === 'undefined' && typeof b.number === 'undefined' )
    return 0;
  // if a is undefined and b isn't a should have a lower index in the array
  else if( typeof a.number === 'undefined' )
    return 1;
    // if b is undefined and a isn't a should have a higher index in the array
  else if( typeof b.number === 'undefined' )
    return -1;
    // if both numbers are defined compare as normal
  else
    return a.number - b.number;
});

console.log( array );


1
有点难以阅读:

var array = [ { number: 2 }, { }, { number: 1 } ];

array.sort((a, b) => 'number' in a ? 'number' in b ? a.number - b.number : -1 : 1);

console.log(array);

"或许更易读一点:

"

var array = [ { number: 2 }, { }, { number: 1 } ];

array.sort((a, b) => !('number' in a) ? 1 : !('number' in b) ? -1 : a.number - b.number);

console.log(array);


0

最简单和更清晰的方式

var array = [
  { number: 2 },
  {},
  { number: 1 }
];
array.sort((a, b) => (a.number == undefined ? Number.MAX_VALUE : a.number) - (b.number  == undefined ? Number.MAX_VALUE : b.number) );

console.log(array);

0

如果提供了compareFunction,则所有非undefined的数组元素将根据比较函数的返回值进行排序(所有undefined元素将按照数组的末尾排序,不调用compareFunction)。

在您的代码中,如果使用数字排序函数对具有未定义值的项不会调用;而在对象数组的情况下,对象已定义但obj.number未定义,因此排序函数也会调用空白({})对象,并需要在sort函数中添加对number的未定义检查。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接