javascript - 按字符串对对象数组进行排序与​​行为不同的数值

标签 javascript typescript

这个问题有点啰嗦,还请多多包涵。

我有一个与 .sort() 一起使用的通用值比较函数:

export function compareObjectValues(key: string, direction: string = 'asc') {
  // used as a comparator supplied to .sort() for sorting arrays of objects

  return function(a: Object, b: Object): number {
    const propertyA = getDescendantProperty(a, key);
    const propertyB = getDescendantProperty(b, key);

    if (!propertyA || !propertyB) {
      return 0;
    }


    const normalizedPropA = (typeof propertyA === 'string') ? (propertyA as string).toLocaleLowerCase() : propertyA;
    const normalizedPropB = (typeof propertyB === 'string') ? (propertyB as string).toLocaleLowerCase() : propertyB;
    let comparison = 0;

    if (normalizedPropA > normalizedPropB) {
      comparison = 1;
    }
    else {
      comparison = -1;
    }

    if (typeof normalizedPropA === 'number' && typeof normalizedPropB === 'number') {
      console.log('comparison', comparison);
      return comparison;
    }
    else {
      console.log('string comparison', comparison);
      return (direction === 'desc') ? (comparison * -1) : comparison;
    }
  };
}

内部辅助函数 getDescendantProperty() 是通过点路径安全地访问对象上的值:

export function getDescendantProperty(obj: object, path: string) {
  // safely access nested properties if the property of the object is itself, an object

  if (path.includes('.')) {
    return path.split('.').reduce((accumulator: object, part: string) => accumulator && accumulator[part] || undefined, obj);
  }
  else {
    return obj[path];
  }
}

当我使用字符串对对象数组进行排序时,compareObjectValues() 按预期工作。使用数值时,它会失败,并且不会遍历所有要比较的预期值。

例子:

describe('on an object that uses a number for sorting', () => {
  it('should return the comparison of the values in the correct order', () => {
    const objects = [
      { id: 4 },
      { id: 0 },
      { id: 2 },
      { id: 3 },
      { id: 1 },
    ];

    const sortedObjects = objects.sort(utils.compareObjectValues('id'));

    expect(sortedObjects).toEqual([{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]);
  });
});

业力输出:

LOG: 'comparison', 1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 0 of 2 SUCCESS (0 secs / 0 secs)
LOG: 'comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 0 of 2 SUCCESS (0 secs / 0 secs)
LOG: 'comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 0 of 2 SUCCESS (0 secs / 0 secs)
Chrome 74.0.3729 (Mac OS X 10.14.5) Utils tests When calling `compareObjectValues` on an object that uses a number for sorting should return the comparison of the numbers in the correct order FAILED
        Error: Expected $[0].id = 4 to equal 0.
        Expected $[1].id = 0 to equal 1.
        Expected $[2].id = 1 to equal 2.
        Expected $[3].id = 2 to equal 3.
        Expected $[4].id = 3 to equal 4.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/lib/utils.spec.ts:133:31)
            at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1)
            at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:308:1)
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
Chrome 74.0.3729 (Mac OS X 10.14.5) Utils tests When calling `compareObjectValues` on an object that uses a number for sorting should return the comparison of the numbers in the correct order FAILED
        Error: Expected $[0].id = 4 to equal 0.
        Expected $[1].id = 0 to equal 1.
        Expected $[2].id = 1 to equal 2.
        Expected $[3].id = 2 to equal 3.
        Expected $[4].id = 3 to equal 4.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/lib/utils.spec.ts:133:31)
            at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1)
LOG: 'string comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', 1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', 1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', 1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', -1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
LOG: 'string comparison', 1
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 1 of 2 (1 FAILED) (0 secs / 0.159 secs)
Chrome 74.0.3729 (Mac OS X 10.14.5): Executed 2 of 2 (1 FAILED) (0.19 secs / 0.16 secs)
TOTAL: 1 FAILED, 1 SUCCESS
TOTAL: 1 FAILED, 1 SUCCESS
npm ERR! Test failed.  See above for more details.

从日志中可以看出,在处理数值对象时,迭代3次就停止了。对于字符串值,它处理了 9。

我已验证 getDescendantProperty() 返回预期值。我还验证了如果我通过在测试中将函数传递给 .sort() 手动应用相同的比较,compareObjectValues() 的逻辑会按预期工作。

我在找出我的错误时遇到了一些困难,非常感谢您的帮助。

最佳答案

问题在于对属性的延迟检查:

if (!propertyA || !propertyB) return 0;

这适用于检查某些类型的值,但在检查数字 (!0 === true)、空字符串 (!'' == = true) 等

改用严格检查:

if (typeof propertyA === 'undefined' || typeof propertyB === 'undefined') return 0;

关于javascript - 按字符串对对象数组进行排序与​​行为不同的数值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56383986/

相关文章:

javascript - window.open 在 Chrome 上的 iFrame 中不起作用

javascript - 你如何在 create-react-app 项目中使用 HJSON 和 Jest?

javascript - Knockout JS 值未显示/绑定(bind)/更新但可通过 ko.toJS($data).value

typescript 只采用接口(interface)中定义的属性

c# - Internet Explorer 无法打开网站

javascript - CreateTextRange 在 Chrome 中不起作用

typescript - 单例服务的 Angular 单元测试

JavaScript - 如何将多个嵌套数组中的所有对象放入一个新的唯一对象数组中

typescript - 使用 '.ts' 中的非 '.tsx'/'ts_library' 文件作为依赖

javascript - 如何在 React JS + Typescript 应用程序中读取本地 JSON 文件并访问数据?