【问题标题】:Cleaning Arrays of Arrays which consist of nils清理由 nil 组成的数组的数组
【发布时间】:2017-04-26 04:28:55
【问题描述】:

例如我有一个数组:

[[nil, nil], [1, 2], [nil, nil], [nil, nil]] 

清洁它的最佳方法是什么?数组必须只有不包含 nil 的数组。清洁后必须:

[[1,2]]

类似:

[[nil, nil], [1, 2], [nil, nil], [nil, nil]].each {|x| x - [nil]} 

【问题讨论】:

  • 子数组[1,2,nil,3]怎么样?

标签: arrays ruby null data-cleaning


【解决方案1】:

删除nil 元素的数组方法称为compact。但是,这对于这种情况还不够,因为您有一个数组数组。此外,您还需要select 非零数组,或reject 数组nil。您可以通过以下方式轻松地将两者结合起来:

[[nil, nil], [1, 2], [nil, nil], [nil, nil]].reject { |arr| arr.compact.empty? }

这仅在您有数字子数组或nils 时才有效。如果您的子数组包含两者,例如[1, nil, 2],那么这个方案会保留整个子数组。

可以在迭代子数组时改变子数组以删除nil,但可以考虑在迭代时进行变异。不过,这样做的方法是使用 bang 版本的 compact 方法,它会改变原始对象:

.reject { |arr| arr.compact!.empty? }

这将采用[[1, 2, nil, 3]] 并返回[[1, 2, 3]]

正如 sagarpandya82 指出的那样,您还可以使用 allany? 方法简单地检查是否所有内容都是 nil,或者是否有任何内容是 nil,而不是删除 nils。

回顾一下:

original_array = [[nil, nil],[1, nil, 2], [1, 2, 3]]
original_array.reject { |arr| arr.all?(:nil) } # returns [[1, nil, 2], [1, 2, 3]]
original_array.reject { |arr| arr.compact.empty? } # returns [[1, nil, 2], [1, 2, 3]]
original_array.reject { |arr| arr.any?(:nil) } # returns [[1, 2, 3]]
original_array.reject { |arr| arr.compact!.empty? } # returns [[1, 2, 3], [1, 2]]

【讨论】:

  • 很高兴看到这样做的递归方式
  • 听起来 OP 想要一个方法链接解决方案,但递归解决方案绝对可以通过方法调用来实现。
【解决方案2】:

假设您只对二维数组感兴趣:

去除仅由nils 组成的子数组:

arr.reject { |arr| arr.all?(&:nil?) }

摆脱由任何nils 组成的子数组:

arr.reject { |arr| arr.any?(&:nil?) }

【讨论】:

    【解决方案3】:
    • compact 将从数组中删除 nil 元素。
    • map 将遍历数组中的每一项,并通过在数组的项上应用代码来返回一个新数组。请注意,在您的示例中 数组元素 本身就是...数组。
    • reject 将返回一个新数组,其中不包含您给定代码回答为“假”的元素。
    • select 将返回一个新数组,其中包含您给定代码“喜欢”的元素(与拒绝相反)。

    因此,如果您只想从数组及其子数组(但不是子子数组)中删除所有nils,您可以调用

    list = [[1,2], [nil], [1,nil,2]]
    list.map(&:compact).reject(&:empty?) #=> [[1,2], [1,2]]
    

    相同
    compacted_list = list.map do |element|
      element.compact
    end
    non_empty_list = compacted_list.reject do |element|
      element.empty?
    end
    

    如果您想从列表/数组中删除所有 [nil, nil] 条目

    list.reject{|element| element == [nil, nil]}
    

    或者如果它更多地是关于选择非零元素(这实际上只是关于意图揭示代码)

    list.select{|element| element != [nil, nil])
    

    这些函数中的大多数都有一个 ! 对应项(如 reject!),它会就地进行修改,这意味着您不必分配返回值(如 new_list = old_list.reject() )。

    【讨论】:

      【解决方案4】:

      似乎对这个问题有不同的解释。

      如果如问题示例所建议的那样,包含一个nil 的所有元素(数组)仅包含nils,并且要排除这些元素,则可以这样做:

      [[nil, nil], [1, 2], [nil, nil], [nil, nil]].select(&:first)
        #=> [!1, 2]]
      

      如果要排除包含至少一个 nil 的所有元素,则可以这样做:

      [[3, nil], [1, 2], [3, 4, 5, nil]].reject { |a| a.any?(&:nil?) }
        #=> [!1, 2]]
      

      如果要从每个元素中删除所有 nils,则可以这样做:

      [[3, nil], [1, 2], [nil], [nil, 3, 4]].map(&:compact)
        #=> [[3], [1, 2], [], [3, 4]]
      

      如果要从每个元素中删除所有nils,然后要删除所有空数组,则可以这样做:

      [[3, nil], [1, 2], [nil], [nil, 3, 4]].map(&:compact).reject(&:empty?)
        #=> [[3], [1, 2], [3, 4]]
      

      【讨论】:

      • &:first 是做什么的? :)
      • @SunilD.,这实际上与arr.select { |pair| pair.first } 相同。 pair.first 既不等于 nil 也不等于 false 时为真。因此,如果a 为真,则选择元素[a,b]。我从问题的示例中假设bnil 当且仅当anil
      【解决方案5】:

      我最近在看facets,这是一个提供大量核心 ruby​​ 语言扩展的 ruby​​ gem。

      他们给出的例子之一是Array#recurse 方法,我将在下面展示它:

      arr = ["a", ["b", "c", nil], nil]
      arr.recurse{ |a| a.compact! }
      #=> ["a", ["b", "c"]]
      

      在您的情况下,这完成了大约一半的工作 - 您还想删除非空数组。

      Facets 通过修补核心 Ruby 方法来工作。这意味着一旦您运行require 'facets/array/recurse',任何先前定义的Array#recurse 方法都将被覆盖。由于命名冲突的可能性,修补核心方法通常是不明智的。

      不过,它仍然是一个有用的方法,并且很容易将其定义为将数组作为参数,而不是对self 的值进行操作。然后,您可以使用它来定义两种方法,共同实现您的目的:

      module ArrayUtils
        def recurse(array, *types, &block)
          types = [array.class] if types.empty?
          a = array.reduce([]) do |arr, value|
            case value
            when *types
              arr << recurse(value, *types, &block)
            else
              arr << value
            end
            arr
          end
          yield a
        end
      
        def recursive_compact(array)
          recurse(array, &:compact)
        end
      
        def recursive_remove_nonempty(array)
          recurse(array) do |arr|
            arr.reject do |x|
              x.is_a?(Array) && x.empty?
            end
          end
        end
      end
      

      测试一下:

      include ArrayUtils
      
      orig = [[nil, nil], [1, 2], [nil, nil], [nil, nil]]
      compacted = recursive_compact(orig)
      nonempties = recursive_remove_nonempty compacted
      
      puts "original: #{orig.inspect}"
      puts "compacted: #{compacted.inspect}"
      puts "nonempties: #{nonempties.inspect}"
      

      并运行

      original: [[nil, nil], [1, 2], [nil, nil], [nil, nil]]
      compacted: [[], [1, 2], [], []]
      nonempties: [[1, 2]]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多