假设:
log = [
{"sasha"=>"HELLO", "robin"=>"HI"},
{"jack"=>"HI", "joey"=>"BYE", "robin"=>"THERE", "lois"=>"HUH" },
{"trixie"=>"HO", "joey"=>"BYE", "robin"=>"THERE","bubba"=>"WHERE"},
{"billie-bob"=>"YO", "bubba"=>"SHARE", "robin"=>"THERE"}
]
据我了解,对于log(散列)中的每一对连续元素,第一个散列的每个键(也是第二个散列的键)将从第一个散列中删除。 (最后,我还考虑删除第一个哈希中存在的最后一个哈希中的键,正如 OP 在评论中提到的那样。)
有很多方法可以做到这一点。这是一个。
new_keys = (log.map(&:keys) << []).each_cons(2).map { |a1,a2| a1-a2 }
#=> [["sasha"], ["jack", "lois"], ["trixie", "joey"],
# ["billie-bob", "bubba", "robin"]]
arr = log.map!.with_index { |h,i| h.slice(*new_keys[i]) }
#=> [{"sasha"=>"HELLO"}, {"jack"=>"HI", "lois"=>"HUH"},
# {"trixie"=>"HO", "joey"=>"BYE"},
# {"billie-bob"=>"YO", "bubba"=>"SHARE", "robin"=>"THERE"}]
我们可以确认原始哈希值已被修改:
arr == log
#=> true
如果希望返回一个新的哈希数组而不改变log,请将Array#map 替换为Array#map!。
参见Enumerable#each_cons 和Hash#slice。
第一个操作,计算每个哈希的新键,包括以下步骤。
a = log.map(&:keys)
#=> [["sasha", "robin"], ["jack", "joey", "robin", "lois"],
# ["trixie", "joey", "robin", "bubba"],
# ["billie-bob", "bubba", "robin"]]
b = a << []
#=> [["sasha", "robin"], ["jack", "joey", "robin", "lois"],
# ["trixie", "joey", "robin", "bubba"],
# ["billie-bob", "bubba", "robin"], []]
enum = b.each_cons(2)
#=> #<Enumerator: [["sasha", "robin"],...
# ["billie-bob", "bubba", "robin"], []]:each_cons(2)>
我们可以看到枚举器enum将生成的元素并通过将其转换为数组传递给map。
enum.to_a
#=> [[["sasha", "robin"], ["jack", "joey", "robin", "lois"]],
# [["jack", "joey", "robin", "lois"],
# ["trixie", "joey", "robin", "bubba"]],
# [["trixie", "joey", "robin", "bubba"],
# ["billie-bob", "bubba", "robin"]],
# [["billie-bob", "bubba", "robin"], []]]
可以看出,生成的每个元素都是一个由两个数组组成的数组,其中包含log 中两个连续哈希的键。继续,
new_keys = enum.map { |a1,a2| a1-a2 }
#=> [["sasha"], ["jack", "lois"], ["trixie", "joey"],
# ["billie-bob", "bubba", "robin"]]
现在应该很明显为什么我们需要enum 生成的最后一个数组来包含log 的最后一个散列的键和一个空数组。这样log 中的最后一个哈希就不会被修改。
第一步不需要为log 的每个元素计算新的键,但这样做的好处是它避免了为每对连续哈希提取键的需要,而这需要@ 987654341@ 操作。
第二个操作,使用new_keys修改已有的hash,包括以下步骤。
enum0 = log.map!
#=> #<Enumerator: [{"sasha"=>"HELLO"},...
# "bubba"=>"SHARE", "robin"=>"THERE"}]:map!>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: [{"sasha"=>"HELLO"},...
# "bubba"=>"SHARE", "robin"=>"THERE"}]:map!>:with_index>
第一个元素由enum1 生成并传递给块(一个二元素数组),它被分成两个元素,这两个元素依次分配给两个块变量。执行此操作的过程称为abbreviated assignment。
h,i = enum1.next
#=> [{"sasha"=>"HELLO"}, 0]
h #=> {"sasha"=>"HELLO"}
i #=> 0
现在执行块计算。
a = new_keys[i]
#=> ["sasha"]
h.slice(*a)
#=> {"sasha"=>"HELLO"}
log 的第一个元素因此被{"sasha"=>"HELLO"} 替换。接下来,
h,i = enum1.next
#=> [{"trixie"=>"HO", "joey"=>"BYE"}, 2]
h #=> {"trixie"=>"HO", "joey"=>"BYE"}
i #=> 2
a = new_keys[i]
#=> ["trixie", "joey"]
h.slice(*a)
#=> {"trixie"=>"HO", "joey"=>"BYE"}
其余步骤类似。
如果还希望从log 的最后一个哈希中删除也存在于log 的第一个哈希中的键,则第一步将更改如下。
new_keys = (log.map(&:keys) << log.first.keys).
each_cons(2).map { |a1,a2| a1-a2 }
#=> [["sasha"], ["jack", "lois"], ["trixie", "joey"],
# ["billie-bob", "bubba"]]
作为
log.map(&:keys) << log.first.keys
#=> [["sasha", "robin"], ["jack", "joey", "robin", "lois"],
# ["trixie", "joey", "robin", "bubba"],
# ["billie-bob", "bubba", "robin"], ["sasha", "robin"]]