1

I'd like to point out I tried quite extensively to find a solution for this and the closest I got was this. However I couldn't see how I could use map to solve my issue here. I'm brand new to Ruby so please bear that in mind.

Here's some code I'm playing with (simplified):

def base_word input
    input_char_array = input.split('') # split string to array of chars
    @file.split("\n").each do |dict_word|
        input_text = input_char_array
        dict_word.split('').each do |char|
            if input_text.include? char.downcase
                input_text.slice!(input_text.index(char))
            end
        end
    end
end

I need to reset the value of input_text back to the original value of input_char_array after each cycle, but from what I gather since Ruby is reference-based, the modifications I make with the line input_text.slice!(input_text.index(char)) are reflected back in the original reference, and I end up assigning input_text to an empty array fairly quickly as a result.

How do I mitigate that? As mentioned I've tried to use .map but maybe I haven't fully wrapped my head around how I ought to go about it.

Community
  • 1
  • 1
gdgr
  • 1,435
  • 2
  • 15
  • 31
  • What are you expecting for output? You're slicing the text, but it doesn't look like it's being printed or returned anywhere. – Matt Nov 09 '15 at 17:48
  • `#map` won't get around the issue in your question, but it could help with output. I'll update my answer with `#map` when you include some info about your expected output. – Matt Nov 09 '15 at 18:06
  • The reason I didn't include anything about that is because I don't really believe it's relevant--my point was that making modifications of any kind to `input text` results in the original value of `input_char_array` also being changed, as such negating what I was trying to achieve in 'resetting' `input-text` – gdgr Nov 10 '15 at 09:15

2 Answers2

1

You can get an independent reference by cloning the array. This, obviously, has some RAM usage implications.

input_text = input_char_array.dup
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Would it make more sense to use `#slice` instead of `#slice!` here, and avoid `#dup`ing altogether? – Matt Nov 09 '15 at 17:45
  • Well, then `slice` will do some copying. – Sergio Tulentsev Nov 09 '15 at 17:47
  • Right, just thinking it's one less method call. Either is valid; wasn't sure if there might be obvious benefit to either approach. – Matt Nov 09 '15 at 17:49
  • I've up-voted this because I found it useful and it helped with my issue--would have been nice to have seen more explanation/possible alternatives/implications though! – gdgr Nov 10 '15 at 09:17
1

The Short and Quite Frankly Not Very Good Answer

Using slice! overwrites the variable in place, equivalent to

input_text = input_text.slice # etc.

If you use plain old slice instead, it won't overwrite input_text.

The Longer and Quite Frankly Much Better Answer

In Ruby, code nested four levels deep is often a smell. Let's refactor, and avoid the need to reset a loop at all.

Instead of splitting the file by newline, we'll use Ruby's built-in file handling module to read through the lines. Memoizing it (the ||= operator) may prevent it from reloading the file each time it's referenced, if we're running this more than once.

def dictionary
  @dict ||= File.open('/path/to/dictionary')
end

We could also immediately make all the words lowercase when we open the file, since every character is downcased individually in the original example.

def downcased_dictionary
  @dict ||= File.open('/path/to/dictionary').each(&:downcase)
end

Next, we'll use Ruby's built-in file and string functions, including #each_char, to do the comparisons and output the results. We don't need to convert any inputs into Arrays (at all!), because #include? works on strings, and #each_char iterates over the characters of a string.

We'll decompose the string-splitting into its own method, so the loop logic and string logic can be understood more clearly.

Lastly, by using #slice instead of #slice!, we don't overwrite input_text and entirely avoid the need to reset the variable later.

def base_word(input)
  input_text = input.to_s # Coerce in case it's not a string
  # Read through each line in the dictionary
  dictionary.each do |word|
    word.each_char {|char| slice_base_word(input_text, char) }
  end
end

def slice_base_word(input, char)
  input.slice(input.index(char)) if input.include?(char)
end
Matt
  • 435
  • 3
  • 9
  • Thanks, Matt. I've up-voted this because it's a really detailed and well-considered answer. I'm still reading through it at the moment--but as a beginner to Ruby this is very helpful because it's introduced a few ideas I might otherwise have taken a while to uncover. – gdgr Nov 10 '15 at 09:19
  • I appreciate you're lacking context to the problem, but this doesn't fully help with the issue I had at hand, so I'll mark Sergio's as the correct answer. Again, thanks for showing me some ideas that will be handy in future though. – gdgr Nov 10 '15 at 09:21