Ruby way to group anagrams in string array - ruby-on-rails

I implemented a function to group anagrams.
In a nutshell:
input: ['cars', 'for', 'potatoes', 'racs', 'four','scar', 'creams', scream']
output: [["cars", "racs", "scar"], ["four"], ["for"], ["potatoes"],["creams", "scream"]]
I would like to know if there is a better way to do this.
I really think I used too much repetition statements: until, select,
delete_if.
Is there any way to combine the select and delete_if statement? That
means, can selected items be automatically deleted?
Code:
def group_anagrams(words)
array = []
until words.empty?
word = words.first
array.push( words.select { |match| word.downcase.chars.sort.join.eql?(match.downcase.chars.sort.join ) } )
words.delete_if { |match| word.downcase.chars.sort.join.eql?(match.downcase.chars.sort.join ) }
end
array
end
Thanks in advance,

Like that:
a = ['cars', 'for', 'potatoes', 'racs', 'four','scar', 'creams', 'scream']
a.group_by { |element| element.downcase.chars.sort }.values
Output is:
[["cars", "racs", "scar"], ["for"], ["potatoes"], ["four"], ["creams", "scream"]]
If you want to you can turn this one-liner to a method of course.

You could use the partition function instead of select, implemented in Enumerable. It splits the entries within the array according to the decision-function into two arrays.
def group_anagrams(words)
array = []
until words.empty?
word = words.first
delta, words = words.partition { |match| word.downcase.chars.sort.join.eql?(match.downcase.chars.sort.join ) } )
array += delta
end
array
end
(untested)

Related

Simpler way to alternate upper and lower case words in a string

I recently solved this problem, but felt there is a simpler way to do it. I looked into inject, step, and map, but couldn't figure out how to implement them into this code. I want to use fewer lines of code than I am now. I'm new to ruby so if the answer is simple I'd love to add it to my toolbag. Thank you in advance.
goal: accept a sentence string as an arg, and return the sentence with words alternating between uppercase and lowercase
def alternating_case(str)
newstr = []
words = str.split
words.each.with_index do |word, i|
if i.even?
newstr << word.upcase
else
newstr << word.downcase
end
end
newstr.join(" ")
end
You could reduce the number of lines in the each_with_index block by using a ternary conditional (true/false ? value_if_true : value_if_false):
words.each.with_index do |word, i|
newstr << i.even? ? word.upcase : word.downcase
end
As for a different way altogether, you could iterate over the initial string, letter-by-letter, and then change the method when you hit a space:
def alternating_case(str)
#downcase = true
new_str = str.map { |letter| set_case(letter)}
end
def set_case(letter)
#downcase != #downcase if letter == ' '
return #downcase ? letter.downcase : letter.upcase
end
We can achieve this by using ruby's Array#cycle.
Array#cycle returns an Enumerator object which calls block for each element of enum repeatedly n times or forever if none or nil is given.
cycle_enum = [:upcase, :downcase].cycle
#=> #<Enumerator: [:upcase, :downcase]:cycle>
5.times.map { cycle_enum.next }
#=> [:upcase, :downcase, :upcase, :downcase, :upcase]
Now, using the above we can write it as following:
word = "dummyword"
cycle_enum = [:upcase, :downcase].cycle
word.chars.map { |c| c.public_send(cycle_enum.next) }.join("")
#=> "DuMmYwOrD"
Note: If you are new to ruby, you may not be familiar with public_send or Enumberable module. You can use the following references.
Enumberable#cycle
#send & #public_send

Call the same function on a list and return a list with no duplicates?

I have this function:
medIntCategory = MedicalInterventionCategory.find_by_category_text(category.category.text)
However now I have a list of categories called categories.
I would like to execute the above code for each category and get back a list of medIntCategories, but with no duplicates.
Is there a simple way to do this since I am only dealing with integers?
in simple terms:
categoryList = []
for each category in categories do
categoryList += MedicalInterventionCategory.find_by_category_text(category.category.text)
end
But with duplicate checking
This sounds like a job for Array#map and Array#uniq:
category_list = categories.map{|category|
MedicalInterventionCategory.find_by_category_text(category.category.text)
}.uniq
#result=Array.new
##assuming that it returns an array
medIntCategory = MedicalInterventionCategory.find_by_category_text(category.category.text)
##get the first category obtained
#result << medIntCategory
if medIntCategory.present?
medIntCategory.each do |m|
##add in same array only if not present
if !#result.include?(m)
#result << m.find_by_category_text(c.category.text)
end
end
##return a unique value array
#result.flatten.compact.uniq unless #result.blank?
end
HOPE IT HELPS
I think this would work
category_list = []
categories.each do |category|
category_list << MedicalInterventionCategory.find_by_category_text(category.category.text).distinct
end

Simplifying an expression using .map

Below I have an example active record query using a bunch of each iterators
user.user_spells.each do |us|
us.spell.buff_effects.where(stat_effected:'gold').each do |be|
value = value + be.value
end
end
I would like to use .map to return a list of all the results so I can do it essentially in one line.
I came up with:
user.user_spells.map { |us| us.spell.buff_effects.where(stat_effected:stat_effected) }.each do |be|
value = value + be.value
end
However... the .map block returns some empty arrays. Not sure how to write it correctly.
Any help would be appreciated! Thanks
Probably along these lines, if what you want is the sum of values in the end:
value =
user.user_spells.flat_map do |us|
us.spell.buff_effects.where(stat_effected:'gold').map(&:value)
end.reduce(&:+)

Return string from multiple array items

I have multiple arrays which have code string items in them. I need to match the code from a given string and then return a class name from the matched array.
Might be better if I show you what I've got. So below are the arrays and underneath this is the string I need to return if the given string matches an item from within the array. So lets say I send a string of '329' this should return 'ss4' as a string:
['392', '227', '179', '176']
= 'ss1'
['389', '386']
= 'ss2'
['371', '338', '335']
= 'ss3'
['368', '350', '332', '329', '323', '185', '182']
= 'ss4'
I need to know what would be the best approach for this. I could create a helper method and have an array for each code block and then check each array to see if the given string code is contained and then return the string, which could be ss1 or ss4. Is this a good idea?
The most efficient approach would be to generate a translator hash once that can perform the lookup super fast:
CODES = {
ss1: ['392', '227', '179', '176'],
ss2: ['389', '386'],
ss3: ['371', '338', '335'],
ss4: ['368', '350', '332', '329', '323', '185', '182']
}
translator = CODES.each_with_object({}){|(s, a), m| a.each{|n| m[n] = s.to_s}}
Now you can simply do:
translator['329']
=> "ss4"
translator['389']
=> "ss2"
def code_to_string(code)
if [395].include? code
"ss1"
elsif [392, 227, 179, 176].include? code
"ss2"
# and so on
end
Note that the codes are integers. to match with a string code, use %w(392 227 179).include? instead of the array
Here's one solution you could try:
CODE_LOOKUP = {
[395] => 'ss1',
[392, 227, 179, 176] => 'ss2',
[389, 386] => 'ss3'
# etc
}
def lookup_code(code)
CODE_LOOKUP.each do |codes_to_test, result|
return result if codes_to_test.include?(code)
end
end
lookup_code(395)
# => "ss1"
lookup_code(179)
# => "ss2"
h = {:ss1 => [395],:ss2 => [392, 227, 179, 176] }
h.key(h.values.find{|x| x.include? "392".to_i})
#=> :ss2
I'd recommend joining all the arrays into a multi-dimensional hash and then searching that.
a1 = ['395']
a2 = ['392', '227', '179', '176']
h = { a1: a1, a2: a2 }
h.select {|a, v| a if v.include?('392') }.keys.first.to_s

Ruby on Rails: Array to Hash with (key, array of values)

Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]

Resources