Search code examples
rubyclosuresstring-parsingidiomsreadability

In Ruby, Idiomatically and cleanly improve on this use of split and map to turn string input to a 2d int array (i.e. a matrix)


I suspect there's a cleaner, more idiomatic, Ruby-way to solve this puzzle than what I've made. I'm too new to Ruby, though, to confirm. All my searches have not addressed the question of how to best write Ruby in the examples I give below.

The puzzle, is I need to take a string-input and turn it into a matrix. Lines are separated by '\n', row-elements are separated by ' '

E.g. the string input "2 6 4\n7 9 8" has output [[2, 6, 4], [7, 9, 8]] I.e. the matrix:

2 6 4
7 9 8

This is my solution:

@rows = in_string.split(/\n/).map{|str| str.split(' ').map{|ch| ch.to_i}}

Explanation of what is happening

  1. "2 6 4\n7 9 8" => split by new-line => ["2 6 4", "7 9 8"]
  2. map over elements from 1. to split by ' '=> [["2", "6", "4"], ["7", "9", "8"]]
  3. map over elements of 2. to become ints (this occurs once for each row)
  4. Fin. We have the result we are after

I feel like this code is not very readable, but I come from rust, so I'm not used to the good style

I'm not familiar enough with Ruby to understand good ways to write these sort of solutions. I've tried using more lines like so

@rows = in_string.split(/\n/).map{
            |str| str.split(' ').map{
                |ch| ch.to_i
            }
         }

But I still feel left in the dark on what might be considered good style, or what other options are available.

The correctness of the code is fine. For me, a great answer is one that will give me an insight in what to look out for when diving into similar problems in the future, and to have more confidence in my understanding of what makes elegant Ruby code.

EDIT from answers Suggestions so far

  • use default for split
  • use defaults on the map
  • use do-end (I'm yet to learn). If someone wants to edit this in, fantastic.

This is what I ended up with: @rows = in_string.split(/\n/).map( |chrs| chrs.split.map( &:to_i ) )


Solution

  • Your code looks fine to me.

    in_string.split(/\n/).map { |row| row.split.map(&:to_i) }
    

    We usually just avoid multiline blocks with {}. Use do-end for multiline blocks.

    Two small improvements I found

    • split default parameter is ' '
    • If you don't need a block don't use it. Just pass the proc to it map(&:to_i)