In Ruby, is there an Array method that combines 'select' and 'map'?

后端 未结 14 795
盖世英雄少女心
盖世英雄少女心 2020-12-07 18:34

I have a Ruby array containing some string values. I need to:

  1. Find all elements that match some predicate
  2. Run the matching elements through a transfo
相关标签:
14条回答
  • 2020-12-07 19:10
    def example
      @lines.select {|line| ... }.map {|line| ... }.uniq.sort
    end
    

    In Ruby 1.9 and 1.8.7, you can also chain and wrap iterators by simply not passing a block to them:

    enum.select.map {|bla| ... }
    

    But it's not really possible in this case, since the types of the block return values of select and map don't match up. It makes more sense for something like this:

    enum.inject.with_index {|(acc, el), idx| ... }
    

    AFAICS, the best you can do is the first example.

    Here's a small example:

    %w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end }
    # => ["a", "b", "c", "d"]
    
    %w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end }
    # => ["A", "B", false, false, "C", "D"]
    

    But what you really want is ["A", "B", "C", "D"].

    0 讨论(0)
  • 2020-12-07 19:11

    Your version:

    def example
      matchingLines = @lines.select{ |line| ... }
      results = matchingLines.map{ |line| ... }
      return results.uniq.sort
    end
    

    My version:

    def example
      results = {}
      @lines.each{ |line| results[line] = true if ... }
      return results.keys.sort
    end
    

    This will do 1 iteration (except the sort), and has the added bonus of keeping uniqueness (if you don't care about uniq, then just make results an array and results.push(line) if ...

    0 讨论(0)
  • 2020-12-07 19:12

    Here is a example. It is not the same as your problem, but may be what you want, or can give a clue to your solution:

    def example
      lines.each do |x|
        new_value = do_transform(x)
        if new_value == some_thing
          return new_value    # here jump out example method directly.
        else
          next                # continue next iterate.
        end
      end
    end
    
    0 讨论(0)
  • 2020-12-07 19:13

    If you have a select that can use the case operator (===), grep is a good alternative:

    p [1,2,'not_a_number',3].grep(Integer){|x| -x } #=> [-1, -2, -3]
    
    p ['1','2','not_a_number','3'].grep(/\D/, &:upcase) #=> ["NOT_A_NUMBER"]
    

    If we need more complex logic we can create lambdas:

    my_favourite_numbers = [1,4,6]
    
    is_a_favourite_number = -> x { my_favourite_numbers.include? x }
    
    make_awesome = -> x { "***#{x}***" }
    
    my_data = [1,2,3,4]
    
    p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]
    
    0 讨论(0)
  • 2020-12-07 19:14

    No, but you can do it like this:

    lines.map { |line| do_some_action if check_some_property  }.reject(&:nil?)
    

    Or even better:

    lines.inject([]) { |all, line| all << line if check_some_property; all }
    
    0 讨论(0)
  • 2020-12-07 19:16

    You should try using my library Rearmed Ruby in which I have added the method Enumerable#select_map. Heres an example:

    items = [{version: "1.1"}, {version: nil}, {version: false}]
    
    items.select_map{|x| x[:version]} #=> [{version: "1.1"}]
    # or without enumerable monkey patch
    Rearmed.select_map(items){|x| x[:version]}
    
    0 讨论(0)
提交回复
热议问题