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:
if !(x == 1 && y == 7)
x == 1
is true andy == 7
is true, therefore!(true && true)
is false, this is skipped.
elsif !(x == 4 && y == 8)
- the
if
was skipped, so theelsif
is evaluated.x == 4
is false (since x is still 1) andy == 8
is false (since y is still 7). Therefore,!(false && false)
is true, and theputs
is reached.
- the
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:
- compute the Cartesian Product of the two arrays:
a.product(b)
- filter out the unwanted pairs:
reject {|x, y| [[1, 7], [4, 8]].any? {|pair| [x, y] == pair } }
- convert to string:
map {|pair| pair.join(' and ') }
- 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 ') }
精彩评论