I have two strings.
str_a = "the_quick_brown_fox"
str_b = "the_quick_red_fox"
I want to find the first index at which the two strings differ (i.e. str_a[i] != str_b[i]).
I know I could solve this with something like the following:
def diff_char_index(str_a, str_b)
arr_a, arr_b = str_a.split(""), str_b.split("")
return -1 unless valid_string?(str_a) && valid_string?(str_b)
arr_a.each_index do |i|
return i unless arr_a[i] == arr_b[i]
end
end
def valid_string?(str)
return false unless str.is_a?(String)
return false unless str.size > 0
true
end
diff_char_index(str_a, str_b) # => 10
Is there a better way to do this?
Something like this ought to work:
str_a.each_char.with_index
.find_index {|char, idx| char != str_b[idx] } || str_a.size
Edit: It works: http://ideone.com/Ttwu1x
Edit 2: My original code returned nil if str_a was shorter than str_b. I've updated it to work correctly (it will return str_a.size, so if e.g. the last index in str_a is 3, it will return 4).
Here's another method that may strike some as slightly simpler:
(0...str_a.size).find {|i| str_a[i] != str_b[i] } || str_a.size
http://ideone.com/275cEU
i = 0
i += 1 while str_a[i] and str_a[i] == str_b[i]
i
str_a = "the_quick_brown_dog"
str_b = "the_quick_red_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 10
str_a = "the_quick_brown_dog"
str_b = "the_quick_brown_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 19
str_a.size
#=> 19
This uses a binary search to find the index where a slice of str_a no longer occurs at the beginning of str_b:
(0..str_a.length).bsearch { |i| str_b.rindex(str_a[0..i]) != 0 }
Related
The goal is to take a string and return the most common letter along with it's count. For string 'hello', it would return ['l', 2].
I've written the following:
def most_common_letter(string)
list = []
bigcount = 0
while 0 < string.length
count = 0
for i in 0..string.length
if string[0] == string[i]
count += 1
end
end
if count > bigcount
bigcount = count
list = (string[0])
string.delete[string[0]]
end
end
return [list,bigcount]
end
I get the following error:
wrong number of arguments (0 for 1+)
(repl):14:in `delete'
(repl):14:in `most_common_letter'
(repl):5:in `initialize'
Please help me understand what I'm doing wrong with the delete statement, or what else is causing this to return an error.
I have a solution done another way, but I thought this would work just fine.
you are using the delete function wrong
Use string.delete(string[0]) instead of string.delete[string[0]]
EDIT
As for the infinite loop you mentioned.
Your condition for while is 0 < string.length
And you expect the string.delete[string[0]] statement to actually delete a character at a time.
But what exactly it does is, it deletes a character and returns the new string, but it never actually mutates/changes the actual string.
So try changing it to string = string.delete[string[0]]
Apart from using delete() instead of delete[] which has already been answered...
Most of what you need is implemented in Ruby's String class natively. each_char and count.
def most_common_letter(string)
max = [ nil, 0 ]
string.each_char {|char|
char_count = string.count(char)
max = [ char, char_count ] if char_count > max[1]
}
return max
end
You may do this in a much easier way, if you allow me to say.
def most_common_letter(string)
h = Hash.new
string.chars.sort.map { |c|
h[c] = 0 if (h[c].nil?)
h[c] = h[c] + 1
}
maxk = nil
maxv = -1
mk = h.keys
mk.each do |k|
if (h[k] > maxv) then
maxk = k
maxv = h[k]
end
end
[ maxk , maxv ]
end
If you test this with
puts most_common_letter("alcachofra")
the result will be
[ 'a', 3 ]
Finally, remember you don't need a return in the end of a Ruby method. The last value assigned is automatically returned.
Do Ruby in a Ruby way!
I want to know if "a" and "z" are together in a string after I have found "a". I'd like to understand why this does not work:
def nearby_az(string)
i = 0
while i < string.length
if string[i] == "a" && string[i+1] == "z"
return true
else
return false
end
i += 1
end
end
I realize there is a simple way to implement this. I am not looking for another solution.
This code will only find "az" if it's at the very beginning. Otherwise it will return false. Postpone return false until you walked the whole string.
def nearby_az(string)
i = 0
while i < string.length -1
return true if string[i] == "a" && string[i+1] == "z"
i += 1
end
# we can only reach this line if the loop above does not return.
# if it doesn't, then the substring we seek is not in the input.
return false
end
nearby_az('baz') # => true
#Segio Tulentsev' s answer explains why yours is broken.
Here's the short implementation if you're interested
def nearby_az(str)
!! str =~ /a(?=z)/i
end
I have two hash arrays like this:
hashArray1 = [{"id"=>"1","data"=>"data1"},{"id"=>"2","data"=>"data2"}]
hashArray2 = [{"id"=>"3","data"=>"data1"},{"id"=>"4","data"=>"data2"}]
I want to compare both of them and return true if everything else matches without the "id" key.
I have tried something like this:
hashArray1.each do |h1|
hashArray2.each do |h2|
if h1.select{|h| h!= "id"} == h2.select{|b| b!= "id"}
break
else
return false
end
end
end
But this seems to be incorrect. Does anyone have a better solution. I am on plain ruby 1.9.3, not using rails framework.
I would simply do:
hash1.zip(hash2).all? do |h1,h2|
return false unless h1.keys == h1.keys
h1.keys.each do |key|
return false if h1[key] != h2[key] unless key == 'id'
end
end
If hash1.length != hash2.length then you can bail out immediately as they can't be the same. If they have the same length then you could do something like this:
except_id = ->(h) { h.reject { |k, v| k == 'id' } }
same = hash1.zip(hash2).find { |h1, h2| except_id[h1] != except_id[h2] }.nil?
If same is true then they're the same (while ignore 'id's), otherwise they're different. Using Hash#reject is one pure Ruby way to non-destructively look at the Hash without a particular key. You could also use:
except_id = lambda { |h| h = h.dup; h.delete('id'); h }
if "copy and remove" makes more sense to you than filtering. If you don't like find then all? might read better:
same = hash1.zip(hash2).all? { |h1, h2| except_id[h1] == except_id[h2] }
or even:
same_without_id = lambda { |h1, h2| except_id[h1] == except_id[h2] }
same == hash1.zip(hash2).all?(&same_without_id)
The question is not necessarily clear, but I assume the order of the hashes are taken into account.
hash1.map{|h| h.reject{|k, _| k == "id"}} ==
hash2.map{|h| h.reject{|k, _| k == "id"}}
I have this array of hashes:
results = [
{"day"=>"2012-08-15", "name"=>"John", "calls"=>"5"},
{"day"=>"2012-08-15", "name"=>"Bill", "calls"=>"8"},
{"day"=>"2012-08-16", "name"=>"Bill", "calls"=>"11"},
]
How can I search the results to find how many calls Bill made on the 15th?
After reading the answers to "Ruby easy search for key-value pair in an array of hashes", I think it might involve expanding upon the following find statement:
results.find { |h| h['day'] == '2012-08-15' }['calls']
You're on the right track!
results.find {|i| i["day"] == "2012-08-15" and i["name"] == "Bill"}["calls"]
# => "8"
results.select { |h| h['day'] == '2012-08-15' && h['name'] == 'Bill' }
.reduce(0) { |res,h| res += h['calls'].to_i } #=> 8
A Really clumsy implementation ;)
def get_calls(hash,name,date)
hash.map{|result| result['calls'].to_i if result['day'] == date && result["name"] == name}.compact.reduce(:+)
end
date = "2012-08-15"
name = "Bill"
puts get_calls(results, name, date)
=> 8
Or another possible way, but a little worse, using inject:
results.inject(0) { |number_of_calls, arr_element| arr_element['day'] == '2012-08-15' ? number_of_calls += 1 : number_of_calls += 0 }
Note that you have to set number_of_calls in each iteration, otherwise it will not work, for example this does NOT work:
p results.inject(0) { |number_of_calls, arr_element| number_of_calls += 1 if arr_element['day'] == '2012-08-15'}
Actually, "reduce" or "inject" is specifically for this exact operation (To reduce the contents of an enumerable down into a single value:
results.reduce(0) do |count, value|
count + ( value["name"]=="Bill" && value["day"] == "2012-08-15" ? value["calls"].to_i : 0)
end
Nice writeup here:
"Understanding map and reduce"
I have a String and I want to get another string out of it which has only characters at odd occuring positions.
For example if i have a string called ABCDEFGH, the output I expect is ACEG since the character indexes are at 0,2,4,6 respectively. I did it using a loop, but there should be one line implementation in Ruby (perhaps using Regex?).
>> "ABCDEFGH".gsub /(.)./,'\1'
=> "ACEG"
Here is one-line solution:
"BLAHBLAH".split('').enum_for(:each_with_index).find_all { |c, i| i % 2 == 0 }.collect(&:first).join
Or:
''.tap do |res|
'BLAHBLAH'.split('').each_with_index do |char, index|
res << c if i % 2 == 0
end
end
One more variant:
"BLAHBLAH".split('').enum_slice(2).collect(&:first).join
Some other ways:
Using Enumerable methods
"BLAHBLAHBLAH".each_char.each_slice(2).map(&:first).join
Using regular expressions:
"BLAHBLAHBLAH".scan(/(.).?/).join
Not sure about the run-time speed but it's one line of processing.
res = "";
"BLAHBLAH".scan(/(.)(.)/) {|a,b| res += a}
res # "BABA"
(0..string.length).each_with_index { |x,i| puts string[x] if i%2 != 0 }