starrr
starrr

Reputation: 1023

Ruby: Reading integers from file and put them into array

I have a file (InputFile.txt) containing blocks of integer numbers that each block is split using * character. Each block contains some rows that each row contains three integers, for example:

4 1233 8

2 55 11

2 4 33

*

3 5 34

2 1 44

6 5 33

*

I want to read each block and put the integers in each block in a 2D array and do some processing on it. I found the following code:

f = File.read 'InputFile.txt'
f.split('*').each  do |set|
  set.split.map(&:to_i)

This code can read each block, But I don't know how to put each block in 2D array. I tried for example set[0][0] and it returns 4, but when I tried set[0][1] it returns blank space. Can anyone tell me what should I do? I want to see 1233 as the second number.

Upvotes: 3

Views: 1397

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110755

If the file is not overly large you can just "gulp" it into a string, split on the asterisks (along with whitespace on each side), and then break up the pieces.

Let's first write some data to file:

str =<<-BITTER_END
4 1233 8
2 55 11

2 4 33
*
3 5 34

2 1 44
6 5 33
*
5 4 1
22 65 98
13 15 71
BITTER_END


FName = 'temp'
IO.write(FName, str) #=> 75

Now we read and process the file:

 IO.read(FName).
   split('*').
   map { |s| s.strip.split(/\n+/).map { |s| s.strip.split.map(&:to_i) } }
  #=> [[[4, 1233, 8], [2, 55, 11], [2, 4, 33]],
  #    [[3, 5, 34], [2, 1, 44], [6, 5, 33]],
  #    [[5, 4, 1], [22, 65, 98], [13, 15, 71]]] 

Here's another to write that. It's more verbose, but it makes it self-documenting, facilitates debugging and testing, and improves maintainability:

class String
  def to_blocks(split_char)
    split(split_char)
  end

  def to_lines
    strip.split(/\n+/)
  end

  def to_arr_of_ints
    strip.split.map(&:to_i)
  end
end

Then:

IO.read(FName).to_blocks('*').map { |block|
  block.to_lines.map { |line|
    line.to_arr_of_ints } }
  #=> [[[4, 1233, 8], [2, 55, 11], [2, 4, 33]],
  #    [[3, 5, 34], [2, 1, 44], [6, 5, 33]],
  #    [[5, 4, 1], [22, 65, 98], [13, 15, 71]]] 

If you'd prefer not to monkey-patch String, you could use Refinements, or just make one argument of each of the three methods a string.

Upvotes: 2

K M Rakibul Islam
K M Rakibul Islam

Reputation: 34336

f = File.read 'InputFile.txt'
data_array = f.split('*').map  do |block|    
    block.split(/\n+/)
    .map { |e| e.split(/\s+/)
    .map(&:to_i) }
    .reject { |a| a.empty? }
end
# => [[[4, 1233, 8], [2, 55, 11], [2, 4, 33]], 
#     [[3, 5, 34], [2, 1, 44], [6, 5, 33]]]

This will work for any number of blocks separated by *. And, in each block, rows are separated by any number of new lines, and in each row, the integer elements are separated by any number of white spaces.

As, each block is a graph, and, three elements in each row represents source_node, dest_node and weight respectively, so, to access these information, you can do the following:

data_array.each_with_index do |row, index|
    p "Graph: #{index}"
    row.each do |inner_row|
        p "source_node: #{inner_row[0]}, dest_node: #{inner_row[1]}, weight: #{inner_row[2]}"
    end
    p '- - -'
end

# => "Graph: 0"
# "source_node: 4, dest_node: 1233, weight: 8"
# "source_node: 2, dest_node: 55, weight: 11"
# "source_node: 2, dest_node: 4, weight: 33"
# "- - -"
# "Graph: 1"
# "source_node: 3, dest_node: 5, weight: 34"
# "source_node: 2, dest_node: 1, weight: 44"
# "source_node: 6, dest_node: 5, weight: 33"

Upvotes: 3

SlySherZ
SlySherZ

Reputation: 1661

f.split("*").each do |block|
    result.push []

    block.split("\n").each do |line|
        result[-1].push line.split.map{ |i| i.to_i } unless line.split.size == 0
    end
end

Upvotes: 1

the happy mamba
the happy mamba

Reputation: 481

Modified your code to add the nested array:

output = []

col = 0
row = 0

f = File.read 'InputFile.txt'
f.split('*').each do|set|
    output[row] = []
    set.split(' ').each do|item|
        output[row][col] = item
        col += 1
    end
    row +=1
    col = 0
end
print output

Result:

[["4", "1233", "8", "2", "55", "11", "2", "4", "33"], ["3", "5", "34", "2", "1", "44", "6", "5", "33"]]

Just make sure not to have a trailing * otherwise it will create an empty inner array. Also wasn't sure if you wanted each new line of numbers in its own array or to read until the next astericks. I assumed read until the astericks.

Upvotes: 1

Related Questions