【问题标题】:Sort Array Of Objects By Number of Keys (JavaScript)按键数对对象数组进行排序 (JavaScript)
【发布时间】:2016-12-23 06:29:36
【问题描述】:

大家好,我有一个对象数组(其中 20 个),我以这种格式放置这些对象以在 Angular 项目中创建列(注意对象中有对象)。我想按具有最多键的对象(在数组中的每个对象内)对它们进行排序,到在数组中的对象内具有最少数字键的对象),以便当它们以列显示时最有意义。

我正在改变一个导航栏变量以用于可点击的高级搜索。感谢您的帮助,因为这是我作为新开发人员的第一个大项目。

var clickableFilters = [
  {"State": {
    "0": [{value: "liquid"}, {count: 30}]
    }
  },
  {"Country": {
    "0": [{value: "USA"}, {count: 50}]
    "1": [{value: "Nigeria"}, {count: 90}]
    }
  },
  {"Color": {
    "0": [{value: "blue"}, {count: 30}]
    "1": [{value: "purple"}, {count: 50}]
    "2": [{value: "green"}, {count: 100}]
    }
  }
 ]

如何按键的数量(内部对象中的键)对对象进行排序,以便最终(在 JavaScript 中)

 [{"Color": {}}, {"Country": {}}, {"State": {}}]

【问题讨论】:

  • “这个问题没有显示任何研究成果”
  • @naomik 这是一个让问题更清楚的例子,他们的名字并不重要。
  • @SpencerWieczorek 是的。仅仅抓住任何对象的第一个属性,不管它可能是糟糕的设计。在某些时候,可能其他一些程序会读取这些对象并需要访问嵌套对象;它应该能够以理智的方式这样做。这是{key: 'x', value: 1}{x:1} 之间的区别——前者比后者好得多。
  • @chrissavage “什么是更合适的密钥名称?” - 只有您可以告诉我们实际密钥是什么。如果是我,如果嵌套对象没有任何用途,我根本不会使用它。如果您需要将一些其他数据与子对象一起附加,那么像 {key: 'somekey', data: childObjectHere} 这样的排序总是可以通过 已知键 data 访问可排序属性,而不仅仅是抓取输入对象的第一个键。
  • @chrissavage 如果您对此有控制权,我认为let filters = [ ..., {filter: 'Country', options: [{value: 'blue', count: 30}, {value: 'purple', count: 50}, ...]}] 是一个更好的数据形状。

标签: javascript arrays sorting object


【解决方案1】:

解决方案 1

Array.prototype.sort 使用自定义比较器可以满足您的需要

let data = [{a:1,b:2,c:3}, {a:1}, {a:1,b:2,c:3,d:4}, {a:1,b:2}]

data.sort((a,b) => Object.keys(a).length - Object.keys(b).length)

console.log(data)

解决方案 2

如果您的数据是嵌套的,则子数据应附加到 已知 名称的属性

let data = [
  {foo: 'W', bar: {a:1,b:2,c:3}},
  {foo: 'X', bar: {a:1}},
  {foo: 'Y', bar: {a:1,b:2,c:3,d:4}},
  {foo: 'Z', bar: {a:1,b:2}}
]

let keyToSort = "bar";
data.sort((a,b) => Object.keys(a[keyToSort]).length - Object.keys(b[keyToSort]).length)

console.log(data)

陷阱和陷阱

另一方面,如果保证始终只有一个键(可能具有未知/动态名称),则可以编写

Object.keys(a[Object.keys(a)[0]]).length

然而,这显然非常丑陋且容易出错(如果它确实有更多键 - 或者根本没有键怎么办)。如果您可以控制数据结构,则应该考虑重构它,因为只有一个键的 Object 没有多大意义 - 您也可以只删除一个嵌套级别。


这是你的未来

你应该养成与复杂性作斗争的习惯——每当它顽固地抬起头来时,抓住你的手杖并对其施加同样顽固的力量。

上面的第一个解决方案似乎有点易于管理,但第二个解决方案开始变得相当厚实。如果您将解决方案分解为可重复使用的微小部分,则可以相对轻松地控制复杂性。

const ascComparator = (a,b) => a < b ? -1 : a > b ? 1 : 0

// use this if you want to sort data descending instead of ascending
const descComparator = (a,b) => ascComparator(b,a)

const prop = x => y => y[x]

const len = prop('length')

const comp = f => g => x => f (g (x))

const keylen = comp (len) (Object.keys)

let data = [
  {foo: 'W', bar: {a:1,b:2,c:3}},
  {foo: 'X', bar: {a:1}},
  {foo: 'Y', bar: {a:1,b:2,c:3,d:4}},
  {foo: 'Z', bar: {a:1,b:2}}
]

// same as solution #2 but much more descriptive
// "sort ascending using keylen of a.bar compared to keylen of b.bar"
data.sort((a,b) => ascComparator(keylen(a.bar), keylen(b.bar)))

console.log(data)

降低复杂性是投资未来的一种方式。一旦你在它自己的过程中包含了一些复杂性,它就会在以后随时供你使用。较小的程序也更容易测试。

ascComparatorproplencompkeylen 上面的每个过程都有立即显而易见的意图。您可以随时重新访问这些内容并轻松理解它们。并且由于使用了它们,它也使你的排序更容易读/写。

【讨论】:

  • 这个答案的问题在于,数组中的每个对象实际上只有一个键,对象为值。
  • 嵌套属性有一些通用的命名约定吗?是这样,那么你应该仍然可以通过使用Object.keys(a.nestedProp).length - Object.keys(b.nestedProp).length 来解决这个问题
  • @chrissavage 你能粘贴父对象的实际密钥吗?我敢肯定它们没有被命名为"least#OfKeysObject""secondMost#OfKeysObj"。实际的键是什么?
  • @blex 我明白了。但你应该了解他们。仅仅抓住第一个财产,无论它是什么,都是随意和糟糕的设计。那是对对象的滥用。这是{key: 'x', value: 1}{x:1}的区别
  • 在我看来是最好的答案,即使它并不直接适用于这个问题。 1) 提到排序 2) 提到自定义比较器函数的概念 3) 将其提炼到最低限度 4) 为 OP 留出一些空间来开发他/她自己的解决方案
【解决方案2】:

对于您的数据结构,您可以像这样使用sort()

var arrayOfObjects = [
  {"least#OfKeysObject": {
    key1: 'value',
    }
  },
  {"secondMost#OfKeysObj": {
    key1: 'value',
    key2: 'value'
    }
  },
  {"most#OfKeysObj": {
    key1: 'value',
    key2: 'value',
    key3: 'value'
    }
  }
 ];
 
 var result = arrayOfObjects.sort(function(a, b) {
   return Object.keys(b[Object.keys(b)[0]]).length - Object.keys(a[Object.keys(a)[0]]).length;
 });
 
 console.log(result)

【讨论】:

  • b[Object.keys(b)] -> b[Object.keys(b)[0]]
  • @Vld:当数组只有一个成员时,toString()的结果是一样的。
  • @squint 我更喜欢明确一点。由于这是一种偏好,因此我将其作为评论留下,而不是建议的编辑。
  • @Christoph:如果对象结构的格式预计总是有一个成员,并且他们得到更多或更少,那么它已经被破坏了。如果没有怎么办? "undefined" 会不会那么坏?如果有多个但他们需要的不是第一个怎么办?
  • 我完全同意。这就是为什么我不喜欢这个答案。然而,添加 [0] 比秘密依赖 toString() 转换更显眼,这对于老手来说是显而易见的,但对于像 OP 这样的新手来说却不是。
【解决方案3】:

试试

arrayOfObjects.sort(function(a, b) {
    return Object.keys(a).length > Object.keys(b).length ? -1 : 1;
});

【讨论】:

  • a 和 b 都相等时会漏掉部分,并且不适用于上面的例子。