0

I have the following (working) code I'm trying to convert into a more concise snippet using either #map or #select.

def duplicate_string(string)
  s_list = []

  string.downcase.chars.each do |char| 
    if string.index(char) != string.rindex(char) && s_list.include?(char) == false
      s_list << char if char != ' '
    end
  end

  s_list
end

puts duplicate_string("I am a duplicate string") == ["i", "a", "t"] #true

This is what I've come up with so far, but I don't know how to access the current array that's been stored by #map or #select and using self isn't working

def duplicate_string_with_map(string)
  string.downcase.chars.select { |char| string.index(char) != string.rindex(char) && self.include?(char) == false && char != ' ' }
end
2
  • Please state your question in words before presenting any code. That tells readers where you are going and allows them to decipher your code more quickly. Also, as a courtesy to readers, please format your code so that horizontal scrolling is not needed to read it. Lastly, it's best not to try to anticipate what form the solution might take. Some might not make use of map or select. Commented Sep 10, 2017 at 17:33
  • If my understand of the question is correct, it could be stated as follows: "I wish to return an array of characters from a given string, after it has been downcased, such that each element c (character) of the array is not a space and is the first of two or more instance of that character in the downcased string." Commented Sep 10, 2017 at 19:23

3 Answers 3

1

The following code would solve your purpose:

def duplicate_string_with_map(string)
  (string.downcase.chars.select { |char| string.index(char) != string.rindex(char) && char != ' ' }).uniq
end

Here you need not check include condition as you are already ensuring string.index(char) != string.rindex(char).

However, for a better ruby approach, I would suggest you to re-open String class and write a method there.

It would look something like this:

class String

  def duplicate_characters_array
    (downcase.chars.select { |char| index(char) != rindex(char) && char != ' ' }).uniq
  end

end

string = "I am a duplicate string"
string.duplicate_characters_array
Sign up to request clarification or add additional context in comments.

1 Comment

Running through all the 2.4.1 string doc to practice all the methods. Haven't gotten to all the array methods yet. Thanks for this :D However, if I wanted to access the array that was being created with #select or #map while the array was still iterating, is that possible to do?
1

You don't need to access the array and you don't need to use Array#map.

There are many ways to reach the goal. One of them is to split the string in chars then group the chars (get a hash), reject the groups of space character and the groups smaller than two elements and return the keys of the remaining groups:

"I am a duplicate string"
    .downcase
    .chars
    .group_by{|i| i}
    .reject{|k, v| k == ' ' || v.length < 2}
    .keys
# ["a", "i", "t"]

Comments

0

Here we can make use of a helper method, Array#difference. The method is explained here. Note that that link contains a link to an SO answer where I cite examples of its use. Though I proposed that the method be added to the Ruby core there appears to be little interest in doing so.

class Array
  def difference(other)
    h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
    reject { |e| h[e] > 0 && h[e] -= 1 }
  end
end

Here we can use this helper as follows.

def duplicate_string(str)
  a = str.gsub(/\s/,'').downcase.reverse.chars
  a.difference(a.uniq).uniq.reverse
end

duplicate_string "I am a duplicate string"
  #=> ["a", "i", "t"]

The steps are as follows.

  str = "I am a duplicate string"
  b = str.gsub(/\s/,'')
    #=> "Iamaduplicatestring"
  c = b.downcase
    #=> "iamaduplicatestring"
  d = c.reverse
    #=> "gnirtsetacilpudamai"
  a = d.chars
    #=> ["g", "n", "i", "r", "t", "s", "e", "t", "a", "c", "i", "l", "p",
    #    "u", "d", "a", "m", "a", "i"]
  e = a.uniq
    #=> ["g", "n", "i", "r", "t", "s", "e", "a", "c", "l", "p", "u", "d", "m"]
  f = a.difference(e)
    #=> ["t", "i", "a", "a", "i"]
  g = f.uniq
    #=> ["t", "i", "a"]
  g.reverse
    #=> ["a", "i", "t"]

The key step is the calculation of f. For each element c of e, f contains n-1 instances of c, where n is the number of instances of c in a. The method therefore excludes characters other than spaces that appear in the string exactly once.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.