开发者

Exclusions in map in Ruby

How do I create an exclusion for a array map in Ruby. Here's what I want to achieve,

a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
  b.map.each do |y|
    if !(x == 1 && y == 7)
      puts "#{x} and #{y}"
    elsif !(x == 4 && y == 8)
      puts "#{x} and #{y}"
    end
  end
end

1 and 5
1 and 6
1 and 7 # still here
1 and 8
2 and 5
2 and 6
2 and 7
2 and 8
3 and 5
3 and 6
3 and 7
3 and 8
4 and 5
4 and 6
4 and 7
4 and 8 # still here

However, it doesn't work, how do I add an exception to these v开发者_StackOverflow社区alues being processed by map? Also if it's possible to use inject/reject/filter function with the same goal.


To explain why 1 and 7 is still printing, step through the logic:

  1. if !(x == 1 && y == 7)
    • x == 1 is true and y == 7 is true, therefore !(true && true) is false, this is skipped.
  2. elsif !(x == 4 && y == 8)
    • the if was skipped, so the elsif is evaluated. x == 4 is false (since x is still 1) and y == 8 is false (since y is still 7). Therefore, !(false && false) is true, and the puts is reached.

Because x can never be both 1 and 4 at the same time and y can never be 7 and 8 at the same time, either your if statement or your elsif statement will always succeed, and since both branches print, the values will be always printed, no matter what x and y are.

As other answers said, you need to combine your clauses.


This is just a problem of not understanding disjunctive semantics in an if statement.

If you want a value NOT to be printed at all, it must match ALL of the negative conditions. Since your predicate is the same (using puts), all you need to do is combine the if statements with an "and" keyword.

That is, something like:

if !(x == 1 && y == 7) and !(x == 4 && y == 8)


I think this does what you want:

a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
  b.map.each do |y|
    if !(x == 1 && y == 7) && !(x == 4 && y == 8)
      puts "#{x} and #{y}"
    end
  end
end

(tested on codepad)

Your old code only tested that !(x == 1 && y == 7) was true OR that !(x == 4 && y == 8) was true - it did not test them both. So when x was 1 and y was 7, the first puts did not execute, but the second one did. Execute this code to trace it better:

a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
  b.map.each do |y|
    if !(x == 1 && y == 7)
      puts "First #{x} and #{y}"
    elsif !(x == 4 && y == 8)
      puts "SEcond #{x} and #{y}"
    end
  end
end


Another variation using a patched Array class and the #reject method.

class Array
  def * arr
    outer = []
    self.each do |a|
      arr.each do |b|
        outer << [a,b]
      end
    end
    outer
  end
end


a = [1,2,3,4]
b = [5,6,7,8]

c = (a * b).reject {|pair| pair == [1,7] || pair == [4,8]}

c.each {|pair| puts "#{pair[0]} and #{pair[1]}"}

Not sure about using the '*' operator, it would need a better implementation if you wanted to keep the ability to multiply arrays by a Fixnum, but i like the syntax for applying methods to a combination of the two arrays which is a fairly common requirement.


I think it would be a good idea to seperate out the seperate steps:

  1. compute the Cartesian Product of the two arrays: a.product(b)
  2. filter out the unwanted pairs: reject {|x, y| [[1, 7], [4, 8]].any? {|pair| [x, y] == pair } }
  3. convert to string: map {|pair| pair.join(' and ') }
  4. print it: puts

That's what we end up with:

puts a.product(b).reject {|x, y|
  [[1, 7], [4, 8]].any? {|pair| [x, y] == pair }
}.map {|pair| pair.join(' and ') }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜