Diana Tokarska
Diana Tokarska

Reputation: 31

How to make an array of class objects in ruby

I'm trying to make an array of class objects, but my code doesn't work. When I make a Solution.new it returns nil, and I want it returns an array of arrays from words in each line of test.txt. I'm using Ruby 2.1.5

class Line 
  def initialize (content)
    @content = content
    self.line_arr
  end
  def line_arr
    @content.split
  end
end

class Solution
  def read_file
    array = []
    File.foreach('test.txt') do |line|
      array << Line.new(line)
    end
  end
end

And now when I make a

foo = Solution.new
foo.read_file

it returns nil.

Upvotes: 2

Views: 1510

Answers (5)

tmn4jq
tmn4jq

Reputation: 361

class Line
  attr_reader :content

  def initialize (content)
    @content = content.split(' ')
  end
end

class Solution
  def read_file
    array = []

    File.foreach('test.txt') do |line|
      array << Line.new(line).content
    end

    array
  end
end

You need to add this 'array' row, because you need to return it from the method call. Also I simplified a bit the Line class here. Basically, this code can solve your problem, but consider using the regular expression for parsing rows.

Upvotes: 0

Anthony E
Anthony E

Reputation: 11235

I don't think Solution.new is returning nil in your example, it's returning a new instance of solution (foo in your example)

Your main issue is that read_file is returning the value of File.foreach, which is always nil.

For starters, update your read_file method to return the array itself:

class Solution
  def read_file
    array = []
    lines = []

    File.foreach('test.txt') do |line|
      lines << Line.new(line)
    end

    array << lines

    array
  end
end

solution = Solution.new
solution.read_file
# outputs:
# [#<Line:0x007fab92163b50 @content="This Is A Line\n">, #<Line:0x007fab92161be8 @content="Line 2\n">, #<Line:0x007fab92160d88 @content="Line3">]

If you want to return an array of arrays split each line by whitespace:

class Solution
  def read_file
    lines = []
    File.foreach('test.txt') do |line|
      words = []
      line.strip.split(/\s+/).each do |word|
        words << word
      end

      lines << Line.new(words)
    end

    lines
  end
end

The key line of code here is: line.strip.split(/\s+/) which first strips leading and trailing whitespace from the string, then converts it to an array by splitting the string based on whitespace (the /s+/ regex matches one or more blank characters).

Some other suggestions:

Pass the filename as an argument to read_file you can set a default argument if you want to:

class Solution
  def read_file(filename = 'test.txt')
    array = []
    File.foreach(filename) do |line|
      array << Line.new(line)
    end

    array
  end
end

Finally, for a much more elegant solution, you can use map, and simply call .split to return a nested array. The Line class isn't really doing much in this case.

class Solution
  def read_file
    File.foreach('test.txt').map do |line|
      line.strip.split(/\s+/)
    end
  end
end

This will simply return an array of arrays, where the inner array contains the words for each line.

Upvotes: 3

Keith Bennett
Keith Bennett

Reputation: 4970

If all you need to do is get arrays of words, and you don't mind loading the entire file into memory at once, then it can be done very simply with the code below (the 3 lines beginning with word_arrays = ..., the rest are setup and output):

#!/usr/bin/env ruby

File.write('woods.txt',
"The woods are lovely, dark, and deep
But I have promises to keep
And miles to go before I sleep
And miles to go before I sleep")

word_arrays = File.readlines('woods.txt').each_with_object([]) do |line, word_arrays|
  word_arrays << line.split
end

word_arrays.each.with_index do |words, index|
  puts "#{index}: #{words} "
end

=begin
Prints:

0: ["The", "woods", "are", "lovely,", "dark,", "and", "deep"]
1: ["But", "I", "have", "promises", "to", "keep"]
2: ["And", "miles", "to", "go", "before", "I", "sleep"]
3: ["And", "miles", "to", "go", "before", "I", "sleep"]
=end

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Consider to use Enumerable#inject instead of creating unnecessary variables:

class Solution
  def read_file
    File.foreach('test.txt').inject([]) do |memo, line|
      memo << Line.new(line)
    end
  end
end

or, in this particular case, map will do the trick:

class Solution
  def read_file
    File.foreach('test.txt').map &Line.method(:new)
  end
end

Upvotes: 0

GeekRiky
GeekRiky

Reputation: 375

Try this:

class Line 
  def initialize (content)
    @content = content
    self.line_arr
  end
  def line_arr
    @content.split
  end
end

class Solution

  def initialize
     self.read_file 
  end

  def read_file
    array = []
    File.foreach('test.txt') do |line|
      array << Line.new(line)
    end
    array
  end
end

Upvotes: 0

Related Questions