【问题标题】:How can I delete keys by iterating over an array of hashes and by considering 2 hashes at a time?如何通过遍历散列数组并一次考虑 2 个散列来删除键?
【发布时间】:2023-03-22 19:54:01
【问题描述】:

我有一个哈希数组。我想对其进行迭代,并希望一次比较 2 个散列,并在两个散列中有相同键时从第一个散列中删除一个键、值对(来自一个散列)。

例如:

log = [{"sasha"=>"HELLO", "robin"=>"HI"},{"jack"=>"HI", "joey"=>"BYE", "robin"=>"THERE"}]

在上面的数组中,两个散列中都有一个键是"robin"。我想从第二个哈希中删除"robin"=>"THERE",而第一个哈希保持不变。

此外,键可以是任何东西。它们不是固定的键名

我已经尝试过(h1.keys & h2.keys).each {|k| puts ( h1[k] == h2[k] ? h1[k] : k ) },但我想看看我是否可以通过编程方式完成它,即使数组中有两个以上的哈希值。

【问题讨论】:

    标签: arrays ruby hash duplicates iteration


    【解决方案1】:

    假设:

    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"=&gt;"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"]] 
    

    【讨论】:

      【解决方案2】:

      遍历连续对并从第二个元素中删除匹配键

      如果您想一次比较哈希数组中的两个数组,您可以使用Enumerable#each_cons 将数组的每个元素与前一个元素进行比较。然后,对于每对连续哈希,只需使用 Hash#delete_if 从 second 哈希中删除匹配的键。

      log = [
        {"sasha"=>"HELLO", "robin"=>"HI"},
        {"jack"=>"HI", "joey"=>"BYE", "robin"=>"THERE"},
      ]
      
      log.each_cons(2) do |hash1, hash2|
        hash2.delete_if { |k,v| hash1.has_key? k }
      end
      
      log
      #=> [{"sasha"=>"HELLO", "robin"=>"HI"}, {"jack"=>"HI", "joey"=>"BYE"}]
      

      【讨论】:

      • 非常感谢@Todd 的回复。但是这个不是比较数组中的第一个和最后一个哈希。它是按顺序进行的。假设我们在一个数组中有 4 个哈希值,那么它需要比较 (1,2)、(2,3)、(3,4) 和 (1,4) 正确。
      • @Robin 你说对了一部分。如果您想将所有散列与所有其他散列进行比较,您可以尝试迭代 Array#permutation,但我认为您有一些尚未解释的基本假设。如果您想删除“重复”键,那么您的原始问题未定义 哪些 值以保留在非连续比较中。
      猜你喜欢
      • 2012-08-03
      • 2023-04-10
      • 2018-06-29
      • 2016-08-03
      • 2019-01-31
      • 2012-08-16
      • 2011-02-03
      • 2014-01-16
      • 1970-01-01
      相关资源
      最近更新 更多