2

I've spent a few hours searching for a way to push an array into another array or into a hash. Apologies in advance if the formatting of this question is bit messy. This is the first time I've asked a question on StackOverflow so I'm trying to get the hang of styling my questions properly.

I have to write some code to make the following test unit past:

class TestNAME < Test::Unit::TestCase
    def test_directions()
        assert_equal(Lexicon.scan("north"), [['direction', 'north']])
        result = Lexicon.scan("north south east")

        assert_equal(result, [['direction', 'north'],
        ['direction', 'south'],
        ['direction', 'east']])

    end
end

The most simple thing I've come up with is below. The first part passes, but then the second part is not returning the expected result when I run rake test.

Instead or returning:

[["direction", "north"], ["direction", "south"], ["direction", "east"]]

it's returning:

["north", "south", "east"]

Although, if I print the result of y as a string to the console, I get 3 separate arrays that are not contained within another array (as below). Why hasn't it printed the outermost square brackets of the array, y?

["direction", "north"] ["direction", "south"] ["direction", "east"]

Below is the code I've written in an attempt to pass the test unit above:

class Lexicon

 def initialize(stuff)
        @words = stuff.split
    end

  def self.scan(word)
    if word.include?(' ')
        broken_words = word.split

      broken_words.each do |word|
        x = ['direction']
        x.push(word)
        y = []
        y.push(x)            
      end
    else
      return [['direction', word]]
    end

  end

end

Any feedback about this will be much appreciated. Thank you all so much in advance.

2 Answers2

2

What you're seeing is the result of each, which returns the thing being iterated over, or in this case, broken_words. What you want is collect which returns the transformed values. Notice in your original, y is never used, it's just thrown out after being composed.

Here's a fixed up version:

class Lexicon
  def initialize(stuff)
    @words = stuff.split
  end

  def self.scan(word)
    broken_words = word.split(/\s+/)

    broken_words.collect do |word|
      [ 'direction', word ]
    end
  end
end

It's worth noting a few things were changed here:

  • Splitting on an arbitrary number of spaces rather than one.
  • Simplifying to a single case instead of two.
  • Eliminating the redundant return statement.

One thing you might consider is using a data structure like { direction: word } instead. That makes referencing values a lot easier since you'd do entry[:direction] avoiding the ambiguous entry[1].

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you so much! I did not know about 'collect'. Very useful method! – Hannah Stephenson Nov 21 '14 at 23:04
  • The [`Enumerable`](http://ruby-doc.org/core-2.1.4/Enumerable.html) module is full of useful methods like this, so if you're learning Ruby spend some time familiarizing yourself with those. Some are very handy. – tadman Nov 22 '14 at 20:33
0

If you're not instantiating Lexicon objects, you can use a Module which may make it more clear that you're not instantiating objects.

Also, there is no need to use an extra variable (i.e. broken_words), and I prefer the { } block syntax over the do..end syntax for functional blocks vs. iterative blocks.

module Lexicon
  def self.scan str
    str.split.map {|word| [ 'direction', word ] }
  end
end

UPDATE: based on Cary's comment (I assume he meant split when he said scan), I've removed the superfluous argument to split.

Brian Adkins
  • 116
  • 6