Okiba Okiba - 6 months ago 9
Ruby Question

Checking cell combination in a table

I'm trying to implement an algorithm for table verification, but I'm confused with the multiple choices Ruby supplies for working with arrays and hashes and I need help putting it all together. Consider this table as example:

| A | B | C |
-------------
| 1 | 2 | 3 |
| 1 | 2 | 4 |
| 5 | 6 | 7 |
| 1 | 1 | 3 |


My method should count the number of occurrences of a specific cell combination. For example:

match_line([A => 1, C => 3])


The results should be
2
, as this combination exists in both the first row and the last one.
What I did so far is to create an
hash
variable that hold column indexing like so:

[A => 0, B => 1, C=> 2]


And I also have an array list which holds all the above table rows like so:

[[1, 2, 3], [1, 2, 4], [5, 6, 7], [1, 1, 3]]


The logic looks like that - the
match_line
method above specific the user wants to match a row where column A has the
1
value in it and column C has the
3
value in it. Based on my index hash, the A column index is
0
and C index is
2
. Now for each array (row) in the array list, if index 0 equals
1
and index 2 equals
3
like the user requested , I add +1 to a counter and keep going over the other array row until I'm over.

I tried to form it into code, but I ended with a way that seems very not efficient of doing so, I'm interested to see your code example to see perhaps Ruby has inner Enumerable methods that I'm not aware of to make it more elegant.

Answer

First, you should use the best available structure to describe your domain :

data = [[1, 2, 3], [1, 2, 4], [5, 6, 7], [1, 1, 3]]

@data_hashes = data.map do |sequence|
  { 'A' => sequence[0], 'B' => sequence[1], 'C' => sequence[2] }
end

Second, I think you should use a real Hash as input for match_line :

# replace match_line([A => 1, C => 3]) with
match_line({'A' => 1, 'C' => 3})

Now you're all set for an easy implementation using Enumerable#select and Array#size

def match_line(match)
  @data_hashes.select { |row|
     match.all? { |match_key, match_value| 
       row[match_key] == match_value
     }
  }.size
end

EDIT: Dynamically create Hash from column names

columns = ['a', 'b', 'c']
data = [[1, 2, 3], [1, 2, 4], [5, 6, 7], [1, 1, 3]]

@data_hashes = data.map do |row|
   Hash[columns.zip(row)]
end
Comments