说我有这个对象集合:
[
{value: 1, contents: "one"},
{value: 2, contents: "two"},
{value: 3, contents: "three"},
{value: 4, contents: "four"},
{value: 5, contents: "five"}
]
并希望将值与内容的关系反转,如下所示:
[
{value: 5, contents: "one"},
{value: 4, contents: "two"},
{value: 3, contents: "three"},
{value: 2, contents: "four"},
{value: 1, contents: "five"}
]
我无法想到实现此目的的算法。我正在使用Ruby,但我不太关心代码,因为我是关于实现这个的方法。
答案 0 :(得分:3)
a = [
{value: 1, contents: "one"},
{value: 2, contents: "two"},
{value: 3, contents: "three"},
{value: 4, contents: "four"},
{value: 5, contents: "five"}
]
a.map{|h| h[:value]}.reverse.zip(a.map{|h| h[:contents]})
.map{|k, v| {value: k, contents: v}}
# =>
# [
# {:value=>5, :contents=>"one"},
# {:value=>4, :contents=>"two"},
# {:value=>3, :contents=>"three"},
# {:value=>2, :contents=>"four"},
# {:value=>1, :contents=>"five"}
#]
或者,
a.each_index.map{|i| {value: a[-i - 1][:value], contents: a[i][:contents]}}
答案 1 :(得分:1)
让arr.each_index.map {|i,a| {value: arr[-1-i][:value], contents: arr[i][:contents]}}
#=> [{:value=>5, :contents=>"one"},
# {:value=>4, :contents=>"two"},
# {:value=>3, :contents=>"three"},
# {:value=>2, :contents=>"four"},
# {:value=>1, :contents=>"five"}]
等于你的哈希数组,这里有几种方法可以做到。
两次通过,没有指数
<a href="javascript:void(0)" onclick="handleViewClick(event)" id="heart" class="next" >
<img src="../images/Love_Heart_SVG1.svg"/>
</a>
一次通过,但不是很漂亮
$('#heart','nHeart').on('click', function(e){
handleViewClick(e);
});
答案 2 :(得分:1)
arr.zip(arr.reverse).map {|a, b| a.merge(value: b[:value]) }
由于reverse
生成了数组的副本,因此这将占用其他方法的两倍的内存 - 对于大多数数据集来说可能根本不是问题。但如果是的话,有一种简单的方法可以避免它。请参阅我的答案末尾的“奖金”部分。
最简单(也可能是最好的)解决方案是遍历数组,并且对于每个项目,从数组另一端的对应项获取:value
。您可以通过从最后一项的索引中减去项目的索引(即数组的大小减1)来获得项目的“对应项”。因此,如果在名为arr
的数组中有五个项目,则算法的步骤如下所示:
end_idx = arr.size - 1 # => 4
new_arr = []
new_arr[0] = { value: arr[end_idx - 0][:value], contents: arr[0][:contents] }
new_arr[1] = { value: arr[end_idx - 1][:value], contents: arr[1][:contents] }
new_arr[2] = { value: arr[end_idx - 2][:value], contents: arr[2][:contents] }
new_arr[3] = { value: arr[end_idx - 3][:value], contents: arr[3][:contents] }
new_arr[4] = { value: arr[end_idx - 4][:value], contents: arr[4][:contents] }
正如你所看到的,每一步都是相同的,但是一个数字递增,所以我打赌你已经知道如何把它变成一个循环:
end_idx = arr.size - 1 # => 4
new_arr = []
0.upto(end_idx) do |idx|
new_arr[idx] = { value: arr[end_idx - idx][:value],
contents: arr[idx][:contents] }
end
简单,诚实是一个非常好的解决方案。但是,它并不是“Rubyish”。我们如何使它更加Rubyish?我很高兴你问过!
作为输出,需要一个数组,其中一个项目对应于输入数组中的每个项目,这是一种非常常见的情况。因为它很常见,所以我们有Enumerable#map
方法,它正是这样做的:它将输入数组(或其他Enumerable)中的每个项“映射”到输出数组中的项目。
map
遍历数组的项目,这正是我们需要的,但它缺少我们需要的一件事:当前项目的索引。为此,我们可以将with_index
方法“链接”到map
方法上,现在,除了数组项本身之外,该块还将传递第二个参数,即它的索引。现在我们拥有了所需的一切:
end_idx = vals.size - 1
arr.map.with_index do |hsh, idx|
{ value: arr[end_idx - idx][:value],
contents: hsh[:contents] }
end
或者,如果我们不想显式指定哈希的结构(如果哈希来自,例如,用户输入或数据库查询,并且可能具有:value
以外的密钥,那么我们可能会这样做{1}}我们希望保留而不必跟踪对输入表单或数据库模式的更改),我们可以这样做:
:contents
但是我终于保存了最好的。
end_idx = vals.size - 1
arr.map.with_index do |hsh, idx|
hsh.merge(value: arr[end_idx - idx][:value])
end
这里发生了什么? Array#zip
方法需要两个数组并将它们“拉上”,例如arr.zip(arr.reverse_each).map do |a, b|
a.merge(value: b[:value])
end
产生[1, 2, 3].zip([:a, :b, :c])
,所以我们用我们的数组及其反向(或者更确切地说,一个Enumerable从数组的末尾产生连续的项,这是[[1, :a], [2, :b], [3, :c]]
返回的内容) ,然后我们使用reverse_each
将map
的值设置为前者的副本(使用:value
)。
加分:为什么merge
而不只是reverse_each
?因为我们可以让它懒惰!假设reverse
有十亿个项目。如果你打电话给arr
,现在你有一个包含20亿个项目的(二维)数组。也许这不是什么大不了的事(或者你可能没有近十亿件物品),但如果是,懒惰可以帮助我们:
arr.zip(arr.reverse)
我们所做的只是添加new_enum = arr.lazy.zip(arr.reverse_each).map do |a, b|
a.merge(value: b[:value])
end
# => #<Enumerator::Lazy: ...>
,现在我们得到一个Enumerator而不是一个数组。在我们调用lazy
或其他一些Enumerable方法之前,这甚至都不会做任何工作,当我们这样做时,它只会按照我们要求的那么多项进行操作。例如,假设我们只想要前三项:
each
由于懒惰,我们从来没有必要复制整个数组并将其反转(并占用相应的内存量);我们只需处理三件事。
如果您确实想要所有项目,但仍想避免复制整个数组,只需致电new_enum.take(3).to_a
# => [ { value: 1000000000, contents: "one" },
# { value: 999999999, contents: "two" },
# { value: 999999998, contents: "three" } ]
。