I have an array:
array = ['Footballs','Baseball','football','Soccer']
and I need to count the number of times Football or Baseball is seen, regardless of case and pluralization.
This is what I tried to do, but with no luck:
array.count { |x| x.downcase.include? 'football' || x.downcase.include? 'baseball' }
What is a right or better way to write this code? I am looking for 3 as an answer.
I would use count combined with a block that checks each element against a regular expression that matches the constraints you're looking for. In this case:
array.count { |element| element.match(/(football|baseball)s?\Z/i) }
This will match any of these elements: football, footballs, baseball, baseballs.
The s? makes the 's' optional, the i option (/i) makes the expression case insensitive, and the \Z option checks for the end of the string.
You can read more about Regexps in the Ruby docs: http://www.ruby-doc.org/core-2.0.0/Regexp.html
A great tool for playing with Regexps is Rubular: http://rubular.com/
If you give a block to the count method of array, it iterates over the array and counts the values for which you return true:
array.count do |x|
(x.downcase.include? 'footbal') || (x.downcase.include? 'baseball')
end
You can use inject to count each item and return the result.
array = ['Football','Baseball','football','Soccer']
count = array.inject({}) do |counter, item|
counter[item.downcase] ||= 0
counter[item.downcase] += 1
counter
end
# => {"football"=>2, "baseball"=>1, "soccer"=>1}
If you need to count a single value, it's even simpler.
array = ['Football','Baseball','football','Soccer']
count = array.inject(0) do |counter, item|
counter += (item.downcase == 'football' ? 1 : 0)
end
On one line
array = ['Football','Baseball','football','Soccer']
count = array.inject(0) { |counter, item| counter += (item.downcase == 'football' ? 1 : 0) }
To include pluralization, simply enhance the comparison.
Assuming you have Ruby on Rails installed for the singularize method (you don't actually need to run this in rails):
require 'active_support/inflector'
array = ['Footballs','Baseball','football','Soccer']
uniq = array.map { |s| s.downcase.singularize }.uniq
uniq.size # => 3
Using Rails and Ruby >= 2.7 you can do:
array = ['Footballs','Baseball','football','Soccer']
array.map(&:downcase).map(&:singularize).tally
=> {"football"=>2, "baseball"=>1, "soccer"=>1}
Related
I'm trying to dynamically generate a case statement based on an array of values. For example let's say I have an array of ranges
[1..3,4..6,7..20,21..38]
and I want to write a dynamic case statement that returns the first number of whatever range
case n
ranges.each do |r|
when r
r.first
end
end
Is this possible, or will I have to find another way to do it (my actual code is more complex)?
If i get your question right, then you can forget case statement and do it using detect:
ary = [1..3, 4..6, 7..20, 21..38]
num = 15 # say
ary.detect { |sub_ary| sub_ary.include?(num) }
=> 7..20
ary.detect { |sub_ary| sub_ary.include?(num) }.first # call `first` on result of above, which is a range, to get the first element.
=> 7
Just out of curiosity:
number = 5
instance_eval [
"case number",
*ranges.map { |r| "when #{r} then (#{r}).first" },
"end"
].join($/)
#⇒ 4
In addition to #detect (or #find) with #include? from Jagdeep Singhs answer you can also use the case equality operator (Range#===). This operator is used by the case statement to compare the input value with the scenario's you're providing.
ranges.find { |range| range === n }.first
Keep in mind both #detect and #find return nil if no value can be found. This means you might want to use the safe navigation operator (}&.first) to prevent a no method exception of #first on nil if the value can't be found.
Well, this works, but is kind of pointless and thread unsafe:
def get_range(n)
ranges = [1..3,4..6,7..20,21..38]
case n
when 3
# special case
199
when ->(x) { #_get_range = ranges.find { |r| r.cover?(x) } }
#_get_range.first
else
0
end
ensure
remove_instance_variable(:#_get_range) if instance_variable_defined?(:#_get_range)
end
get_range(3) # => 199
get_range(5) # => 4
get_range(50) # => 0
You could just do:
ranges.find { |r| r.cover?(n) }&.first || 0
My two cents..
ranges = [1..3,4..6,7..20,21..38]
num = 15
ranges.bsearch { |range| range.member? num }.begin
My Ruby assignment is to iterate through a hash and return the key associated with the lowest value, without using any of the following methods:
#keys #values #min #sort #min_by
I don't understand how to iterate through the hash and store each pair as it comes through, compare it to the last pair that came through, and return the lowest key. This is my code to show you my thought process, but it of course does not work. Any thoughts on how to do this? Thanks!
def key_for_min_value(name_hash)
index = 0
lowest_hash = {}
name_hash.collect do |key, value|
if value[index] < value[index + 1]
lowest = value
index = index + 1
key_for_min_value[value]
return lowest
end
end
end
Track min_value and key_for_min_value. Iterate through the hash, and any time the current value is lower than min_value, update both of these vars. At the end of the loop, return key_for_min_value.
I didn't include sample code because, hey, this is homework. :) Good luck!
One way to do it is transforming our hash into an array;
def key_for_min_value(name_hash)
# Convert hash to array
name_a = name_hash.to_a
# Default key value
d_value= 1000
d_key= 0
# Iterate new array
name_a.each do |i|
# If current value is lower than default, change value&key
if i[1] < d_value
d_value = i[1]
d_key = i[0]
end
end
return d_key
end
You might need to change d_value to something higher or find something more creative :)
We can use Enumerable#reduce method to compare entries and pick the smallest value. Each hash entry gets passed in as an array with 2 elements in reduce method, hence, I am using Array#first and Array#last methods to access key and values.
h = {"a" => 1, "b" => 2, "c" => 0}
p h.reduce{ |f, s| f.last > s.last ? s : f }.first
#=> "c"
Given an array like : [0,1,1]
How can I elegantly check that: Only one element has a non-zero value and that the others are 0?
(So the above array will fail the check while this array will pass : [1,0,0])
my_array.count(0) == my_array.length-1
If speed is important, for very large arrays where you might need to return early upon detecting a second non-zero, perhaps:
def only_one_non_zero?( array )
found_non_zero = false
array.each do |val|
if val!=0
return false if found_non_zero
found_non_zero = true
end
end
found_non_zero
end
Select at most two non-zero elements, and check if exactly one item was available.
>> [0,1,1].select {|x| !x.zero?}.take(2).size == 1
=> false
>> [0,1,0].select {|x| !x.zero?}.take(2).size == 1
=> true
>> [1,2,3].select {|x| !x.zero?}.take(2).size == 1
=> false
Works fine in Ruby 1.8.7, but note that select returns an array, so it's not "optimally lazy". Here's a blog post showing how to make some lazy enumerators in Ruby.
Thanks for all your answers!
I solved too:
input_array = [0,0,0]
result = input_array - [0]
p result.size == 1 && result[0] == 1
Ruby, I love you!
I came with below solution but I believe that must be nicer one out there ...
array = [ 'first','middle','last']
index = array.length
array.length.times { index -= 1; puts array[index]}
Ruby is smart
a = [ "a", "b", "c" ]
a.reverse_each {|x| print x, " " }
array.reverse.each { |x| puts x }
In case you want to iterate through a range in reverse then use:
(0..5).reverse_each do |i|
# do something
end
You can even use a for loop
array = [ 'first','middle','last']
for each in array.reverse do
print array
end
will print
last
middle
first
If you want to achieve the same without using reverse [Sometimes this question comes in interviews]. We need to use basic logic.
array can be accessed through index
set the index to length of array and then decrements by 1 until index reaches 0
output to screen or a new array or use the loop to perform any logic.
def reverseArray(input)
output = []
index = input.length - 1 #since 0 based index and iterating from
last to first
loop do
output << input[index]
index -= 1
break if index < 0
end
output
end
array = ["first","middle","last"]
reverseArray array #outputs: ["last","middle","first"]
In a jade template you can use:
for item in array.reverse()
item
You can use "unshift" method to iterate and add items to new "reversed" array.
Unshift will add a new item to the beginning of an array.
While << - adding in the end of an array. Thats why unshift here is good.
array = [ 'first','middle','last']
output = []
# or:
for item in array # array.each do |item|
output.unshift(item) # output.unshift(item)
end # end
puts "Reversed array: #{output}"
will print: ["last", "middle", "first"]
We can also use "until":
index = array.length - 1
until index == -1
p arr[index]
index -= 1
end
I am trying to return the index's to all occurrences of a specific character in a string using Ruby. A example string is "a#asg#sdfg#d##" and the expected return is [1,5,10,12,13] when searching for # characters. The following code does the job but there must be a simpler way of doing this?
def occurances (line)
index = 0
all_index = []
line.each_byte do |x|
if x == '#'[0] then
all_index << index
end
index += 1
end
all_index
end
s = "a#asg#sdfg#d##"
a = (0 ... s.length).find_all { |i| s[i,1] == '#' }
require 'enumerator' # Needed in 1.8.6 only
"1#3#a#".enum_for(:scan,/#/).map { Regexp.last_match.begin(0) }
#=> [1, 3, 5]
ETA: This works by creating an Enumerator that uses scan(/#/) as its each method.
scan yields each occurence of the specified pattern (in this case /#/) and inside the block you can call Regexp.last_match to access the MatchData object for the match.
MatchData#begin(0) returns the index where the match begins and since we used map on the enumerator, we get an array of those indices back.
Here's a less-fancy way:
i = -1
all = []
while i = x.index('#',i+1)
all << i
end
all
In a quick speed test this was about 3.3x faster than FM's find_all method, and about 2.5x faster than sepp2k's enum_for method.
Here's a long method chain:
"a#asg#sdfg#d##".
each_char.
each_with_index.
inject([]) do |indices, (char, idx)|
indices << idx if char == "#"
indices
end
# => [1, 5, 10, 12, 13]
requires 1.8.7+
Another solution derived from FMc's answer:
s = "a#asg#sdfg#d##"
q = []
s.length.times {|i| q << i if s[i,1] == '#'}
I love that Ruby never has only one way of doing something!
Here's a solution for massive strings. I'm doing text finds on 4.5MB text strings and the other solutions grind to a halt. This takes advantage of the fact that ruby .split is very efficient compared to string comparisions.
def indices_of_matches(str, target)
cuts = (str + (target.hash.to_s.gsub(target,''))).split(target)[0..-2]
indicies = []
loc = 0
cuts.each do |cut|
loc = loc + cut.size
indicies << loc
loc = loc + target.size
end
return indicies
end
It's basically using the horsepower behind the .split method, then using the separate parts and the length of the searched string to work out locations. I've gone from 30 seconds using various methods to instantaneous on extremely large strings.
I'm sure there's a better way to do it, but:
(str + (target.hash.to_s.gsub(target,'')))
adds something to the end of the string in case the target is at the end (and the way split works), but have to also make sure that the "random" addition doesn't contain the target itself.
indices_of_matches("a#asg#sdfg#d##","#")
=> [1, 5, 10, 12, 13]