【问题标题】:Sort Map/Hash by values, retaining keys按值对 Map/Hash 排序,保留键
【发布时间】:2016-07-04 21:49:15
【问题描述】:

我正在寻找这个问题的答案。

我想在 JavaScript 中有一个哈希,像这样:

const hash = {
   a: 'A',
   c: 'C',
   b: 'B'
}

我想按对对象进行排序,这样它可能变成:

const hash = {
   a: 'A',
   b: 'B',
   c: 'C',
}

现在,我意识到 POJSO 不保证其键的顺序,所以我正在考虑使用 immutableJS 或一些可以保证散列中键的顺序的对象。

所以我正在寻找类似的东西:

var newHash = Immutable.Map(hash).sortBy(function(key,val){
       return val;
});

但我不相信这存在。 这要求太多了吗?我怎样才能找到这个功能?

【问题讨论】:

  • 除了,大声笑我想按值排序
  • 为什么要这样做?我觉得如果我们知道潜在的动机,我们可能会为您指出一些更有用的东西......
  • 改变key的值并没有那么复杂... ;)
  • 我想这样做的原因是因为哈希是用户输入我正在编写的库的配置输入的最方便的数据结构,不幸的是,解释这将远远超出问题的范围

标签: javascript underscore.js lodash immutable.js


【解决方案1】:

现在,我意识到 POJSO 不保证其密钥的顺序

他们现在这样做了,从 ES2015 开始。您仍然无法通过for-inObject.keys 访问该订单,但您可以通过Object.getOwnPropertyNames 访问该订单。该顺序由规范操作[[OwnPropertyKeys]] 给出。忽略继承的属性,它基本上是: 作为整数索引的字符串的键,按数字顺序;后面是字符串的剩余键,按照创建属性的顺序;后跟 Symbols 的键,按创建顺序排列。

有了这些信息,如果您的键都是不符合整数索引条件的字符串,那么在符合 ES2015 的引擎上,我们可以这样做:

  1. 以任意顺序从属性开始(例如,acb

  2. 使用Object.keys 将这些键作为数组获取

  3. 使用Array#map 创建一个对象数组,其中包含键和值的属性(我在下面使用了kv

  4. 按值对数组进行排序

  5. 使用Array#reduce 创建一个新对象,并按排序顺序向其中添加属性。 (包括我自己在内的一些人可能会争辩说这是对reduce 的轻微滥用,因为累加器实际上从未改变过,但是嘿,它让我们成为 l33t [这仍然是一件事吗?] 并避免中间变量。嗯,因为这是一个目标?:-))

最终结果是一个对象,其属性按创建顺序排列,这是它们的值的顺序,因为我们是按该顺序创建它们的。我们可以通过Object.getOwnPropertyNames 访问该订单。

// The unsorted data
let data = {
  a: 'A',
  c: 'C',
  b: 'B'
};

// Create it sorted
const hash = Object.keys(data)
  .map(key => ({k: key, v: data[key]}))
  .sort((a, b) => a.v.localeCompare(b.v))
  .reduce((o, e) => {
    o[e.k] = e.v;
    return o;
  }, {});

// Display the sorted result
Object.getOwnPropertyNames(hash).forEach(n => {
  console.log("hash['" + n + "'] = " + hash[n]);
});

除此之外,正如我在对该问题的评论中所说,我不确定您为什么要这样做。看来如果我们知道您的最终目标是什么,我们或许可以为您指明一个更有用的方向……

【讨论】:

  • 嗯,促进减少? ;)
  • @NinaScholz:大声笑,你看到我的评论了吗?我感到很内疚,使用reduce 和一个不变的累加器......
  • @NinaScholz:至少我没有在其中添加逗号运算符:.reduce((o, e) => (o[e.k] = e.v, o), {}) ;-)
  • 为什么不呢?逗号旁边是 void 一些最容易被误解的 js 特性。
  • @NinaScholz:这就是为什么不这样做。 :-) 那对我来说可读性是不可接受的。但每个人都有自己的看法。
【解决方案2】:

Lodash 单线:

sorted = _(hash).toPairs().sortBy(1).fromPairs().value();

现场示例:

var data = {a: 'A', c: 'C', b: 'B'};
var hash = _(data).toPairs().sortBy(1).fromPairs().value();
Object.getOwnPropertyNames(hash).forEach(n => {
  snippet.log("hash['" + n + "'] = " + hash[n]);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

【讨论】:

  • 我尊重你的观点,但我不得不说哎呀,这太恶心了,为什么没有更好的方法来用 JS 做到这一点超出了我的能力。
  • @AlexMills:因为 JavaScript 对象不是哈希映射,而且几乎没有任何理由希望对象属性按特定顺序排列。
  • @AlexMills:你的意思是更好的方法?没有内置函数,因为这是一个愚蠢的想法。你最终想要做什么?
  • 很好的答案,使用 OP 已经拥有的库的力量。我建议列出警告或链接到我的答案,说“请参阅此处的警告。”
  • 实际上,我还需要 Promise.all 来处理具有值作为承诺的对象,而不仅仅是承诺数组,但它没有实现。我不是在做 Web 编程,我正在为 node.js 创建一个精美的测试库,这就是为什么我的用例有点出众的原因。
猜你喜欢
  • 2018-07-26
  • 2021-11-22
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-19
  • 2021-06-17
  • 2014-03-21
相关资源
最近更新 更多