I have a 3d array, which I need to check the 2nd 'dimension' of against a partially known value. Illustration of array composition below:
a = [[[1,1], nil]], [[2,3], [4,8]], [[6,1],[9,9]], [[5,7], nil]]]
I need to check this array for presence of an array containing two values, the first of which I'll know the exact contents of, but the second of which all I know is that it'll either be nil
or a 2 element array with unknown single digit values. In pseudo code what I'm trying to do is; a.include?([[1,1],*unknown*])
.
Maybe regular expression matcher is the way to go here (?). Perhaps something like;
a.include?([[1,1],/\[\d,\d\]||(nil)])
(I'm trying to say either; [any single 0-9 digit, any single 0-9 digit] or 'nil')
Any help appreciated.
While "Find Pattern" is still experimental Your use case is perfect for Pattern matching
For Example:
arr = [[[1,1], nil], [[2,3], [4,8]], [[6,1], [9,9]],
[[2,3], [3,7]], [[5,7], nil],[[7,9],[42,1]]]
def matches_known_pattern?(arr,known)
arr in [*,[^known,nil|[0..9,0..9]],*]
end
matches_known_pattern?(arr,[1,1]) #=> true
matches_known_pattern?(arr,[2,3]) #=> true
matches_known_pattern?(arr,[7,8]) #=> false
matches_known_pattern?(arr,[7,9]) #=> false because 42 does not match 0..9
matches_known_pattern?(arr,[1,'a']) #=> false
The pattern is fairly self explanatory but it breaks down as a[0] == known
and a[1] == nil
or a[1]
is an array of any 2 Integers Numerics (Including Integer
, Float
, etc.) between 0 and 9. If you want to allow any Integer you can replace [0..9,0..9]
with [Integer,Integer]
. If you don't care at all what a[1]
is then you can use *
(which comes reasonably close to your proposed pattern of [[1,1],*unknown*]
)
The [*,...,*]
on either side is the "find pattern" part that is experimental. Essentially it just means find this pattern anywhere in arr
(first match wins).
One other note is ^known
because pattern matching allows for variable binding without the caret (^
) the variable known
would be assigned the value of the first element (a[0][0]
) in the pattern. The ^
"pins" the local variable known
or as described in the docs "For this case, the pin operator ^
can be used, to tell Ruby 'just use this value as part of the pattern'"
Thank you @steenslag for pointing out that Find Pattern is no longer experimental
UPDATE
To cover the case @CarySwoveland pointed out ([[8,8],[1,4.6]]
) this would need to be changed to:
case arr
in [*,[^known,[Integer=>a,Integer=>b]],*]
(0..9).cover?(a) && (0..9).cover?(b)
in [*,[^known,nil],*]
true
else
false
end
Where the Integer
and nil
checks are separated because variable binding is not available for alternate expressions (separated with |
)