我们假设我需要对Hash
的每个元素执行一项简单的任务,例如将其值增加1
,或将值更改为包含该值的数组。我一直在这样做
hash.map{ |k, v| [k, v+1] }.to_h
v+1
只是一个例子,它可以是任何东西。
有没有更干净的方法呢?我不太喜欢将哈希映射到2个大小的数组数组,然后记住再次将其转换为哈希值。
可能更好的例子:
hash.hash_map{ |v| v+1 }
这样一些字符串转换(to_s
)可能会简化为
hash.hash_map(&:to_s)
重复说明:
我不是在寻找Hash[...]
或.to_h
,而是在询问是否有人知道更紧凑,更清洁的解决方案。
答案 0 :(得分:4)
这就是Ruby的集合框架的工作方式。 map
中有一个 Enumerable
方法,它不知道任何关于哈希或数组或列表或集合,树或流或其他任何您可能想到的内容。它只知道有一个名为each
的方法,每次迭代将yield
一个单个元素。就是这样。
请注意,这与Java和.NET的集合框架的工作方式相同。所有集合操作总是返回相同的类型:在.NET中,即IEnumerable
,在Ruby中,即Array
。
另一种设计方法是集合操作是类型保留的,即映射集合将产生集合等等。例如,这就是在Smalltalk中完成的方式。然而,在Smalltalk中,它通过将几乎相同的方法复制和粘贴到每个不同的集合中来实现。即如果你想在Ruby中实现你自己的集合,你只需要实现each
,你就可以免费获得其他所有东西,而在Smalltalk中,你必须分别实现每一个集合方法。 (在Ruby中,这将超过40种方法。)
Scala是第一种设法为集合框架提供类型保留操作而没有代码重复的语言,但直到Scala 2.8(2010年发布)才解决这个问题。 (关键是集合构建器的想法。)Ruby的集合库是在1993年设计的,17年前我们已经想出如何在没有代码重复的情况下进行类型保留集合操作。此外,Scala在很大程度上依赖于其复杂的静态类型系统和类型级元编程,以便在编译时找到正确的集合构建器。这对于该方案来说不是必需的,但是必须在运行时为每个操作查找构建器可能会产生大量的运行时成本。
您可以做的是添加不属于标准Enumerable
协议的新方法,例如类似于Scala的mapValues
和mapKeys
。< / p>
答案 1 :(得分:3)
AFAIK,这在Ruby框中的Hash
中不存在,但这是一个简单的monkeypatch来实现你想要的:
▶ class Hash
▷ def hash_map &cb
▷ keys.zip(values.map(&cb)).to_h
▷ end
▷ end
有更多可读的方法来实现所请求的功能,但是这个方法使用内置的map
值一次,假装是我脑海中最快的实现。
▶ h = {a: 1, b: 2}
#⇒ { :a => 1, :b => 2 }
▶ h.hash_map do |v| v + 5 end
#⇒ { :a => 6, :b => 7 }