logo
Published on

对象数组按照字符串属性值排序

Authors
  • Name
    Twitter

在处理复杂数据结构时,例如对象数组,常常需要根据嵌套的属性值进行排序。本文将详细介绍如何实现这一功能,包括各个函数的具体实现步骤。

问题描述

我们遇到了如下的数据结构,需要对其进行排序。我们不希望临时展平对象,也不打算使用像 underscorelodash 这样的库,主要是出于性能考虑以及为了实现的乐趣。

var People = [
  { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
  { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
  { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' },
]

目标

我们的目标是首先根据 People.Name.name 进行排序,其次再根据 People.Name.surname 进行排序。

障碍

基于原始解决方案,通常使用方括号表示法来动态计算需要排序的属性。然而,这里我们需要也动态构建方括号表示法,因为你可能期望类似 People['Name.name'] 这样的语法可以工作,但实际上不能。直接使用 People['Name']['name'] 是静态的,仅允许你深入到第 n 级。

解决方案

为了实现目标,我们需要遍历对象树并确定最终叶节点的值,同时也要处理任何中间叶节点。

var People = [
  { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
  { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
  { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' },
]

People.sort(dynamicMultiSort(['Name', 'name'], ['Name', '-surname']))

// 结果是:
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

function dynamicSort(properties) {
  var sortOrder = 1
  if (properties[properties.length - 1][0] === '-') {
    sortOrder = -1
    properties[properties.length - 1] = properties[properties.length - 1].substr(1)
  }
  return function (a, b) {
    var propertyOfA = recurseObjProp(a, properties)
    var propertyOfB = recurseObjProp(b, properties)
    var result = propertyOfA < propertyOfB ? -1 : propertyOfA > propertyOfB ? 1 : 0
    return result * sortOrder
  }
}

function recurseObjProp(root, leafs, index) {
  index = index || 0
  var upper = root
  var lower = upper[leafs[index]]
  if (!lower) {
    return upper
  }
  index++
  return recurseObjProp(lower, leafs, index)
}

function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments)
  return function (a, b) {
    var i = 0,
      result = 0,
      numberOfProperties = args.length
    while (result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b)
      i++
    }
    return result
  }
}

示例

可在 JSBin 上查看工作示例。