Use case for `&&=` - ruby

I have seen and used by myself lots of ||= in Ruby code, but I have almost never seen or used &&= in practical application. Is there a use case for &&=?

Not to be glib, but the obvious use case is any time you would say x && foo and desire the result stored back into x. Here's one:
list = [[:foo,1],[:bar,2]]
result = list.find{ |e| e.first == term }
result &&= result.last # nil or the value part of the found tuple

any sort of iteration where you want to ensure that the evaluation of a boolean condition on all elements returns true for all the elements.
e.g.
result = true
array.each do |elem|
# ...
result &&= condition(elem) # boolean condition based on element value
end
# result is true only if all elements return true for the given condition

Validation of the document such as,
a = {'a' => ['string',123]}
where the element in a['a'] should be a String. To validate them, I think you can use,
def validate(doc, type)
valid = true
doc.each{|x|
valid &&= x.is_a? type
}
valid
end
1.9.3p392 :010 > validate(a['a'],String) => false

Related

Processing Array and returning boolean value

I'm wondering, what would be the easiest way to check if all the elements of Array conform to certain criteria and return boolean? Is there maybe a pattern in Ruby to call method on collection and then return boolean value? Standard Enumerable methods return either Array or nil, so I'm not sure where to look.I've wrote an example that works using grep, but I feel that if could be skipped with more idiomatic code:
def all_matched_by_regex?(regex)
array_collection = ['test', 'test12', '12test']
matched = array_collection.grep(regex)
if matched.length == array_collection.length
return true
end
return false
end
Did you try Enumerable.all? {block} ? It seems like exactly what you're looking for.
Edit:
My Ruby is a bit rusty, but here's an example of how to use it
regex = /test/
=> /test/
array_collection = ['test', 'test12', '12test']
=> ["test", "test12", "12test"]
array_collection.all? {|obj| regex =~ obj}
=> true
You can change:
if matched.length == array_collection.length
return true
end
return false
with simply returning:
matched.length == array_collection.length
Like this:
def all_matched_by_regex?(regex)
array_collection = ['test', 'test12', '12test']
matched = array_collection.grep(regex)
matched.length == array_collection.length
end

How can I check to see if a pair of words are only 1 letter different?

In writing a method to compare 2 words, how can I check to see if the words are only 1 letter different? I'm assuming words are same length and order of letters doesnt matter (see "cobra","bravo").
def one_letter_apart?(word1, word2)
I expect the results below:
one_letter_apart?("abra","abro") == true
one_letter_apart?("cobra","bravo") == true
one_letter_apart?("bravo","tabby") == false
one_letter_apart?("abc","cab") == false
I have tried a few ways of manipulating them (splitting,sorting,then setting equal and adding to new array, then counting), but none so far have worked. Any ideas are greatly appreciated.
This one makes use of the fact that String#sub substitutes only the first thing it finds.
def one_different_char?(str, other)
other_str = other.dup
str.chars{|char| other_str.sub!(char, '')} #sub! just replaces just one occurence of char
other_str.size == 1
end
test_set = [["abra","abro"],["cobra","bravo"],["bravo","tabby"],["abc","cab"]]
test_set.each{|first, second| puts one_different_char?(first, second) }
#true
#true
#false
#false
Check Levenshtein Distance
You want the Levenstein distance. For example, using the text gem:
require 'text'
def one_letter_apart? string1, string2
Text::Levenshtein.distance(string1, string2).eql? 1
end
one_letter_apart? "abra", "abro"
# => true
one_letter_apart? "cobra", "bravo"
# => false
def one_letter_apart?(s1, s2)
return false if s1.length != s2.length
a2 = s2.chars.to_a
s1.chars.each do |c|
if i = a2.index(c)
a2.delete_at(i)
end
end
a2.length == 1
end
one_letter_apart?("abra","abro") == true
# => true
one_letter_apart?("cobra","bravo") == true
# => true
one_letter_apart?("bravo","tabby") == false
# => true
one_letter_apart?("abc","cab") == false
# => true
Update: To answer your question of how it works: This is the exact same general algorithm as steenslag's, but I didn't think of using String#sub! to do the removal, so I converted to arrays and used a combination of index and delete_at to remove the first occurrence of the given character. The naïve approach is a2.delete_at(a2.index(c)), but if the character c doesn't exist in a2, then index returns nil, which is an invalid input for delete_at. The workaround is to only call delete_at if index returns something non-nil, which is what I've done. i is declared and set to a2.index(c), and the value of that assignment is evaluated by if. It's the same as:
i = a2.index(c)
if i
# ...
I much prefer steenslag's approach and would have done the exact same thing if I'd thought of String#sub!.
This function returns true if two strings have equal lengths and only one different letter while all the other letters are in the same positions:
def one_letter_apart? string1, string2
return false if string1.size != string2.size
found = false
(0...string1.size).each do |i|
next if string1[i] == string1[i]
return false if found # if found is already true, and we found another difference, then result is false.
found = true # We found the first difference.
end
found # True if only one difference was found.
end
This function handles letters in wrong positions (like "cobra" and "bravo") as well:
def one_letter_apart? string1, string2
letters1 = string1.chars.each_with_object(Hash.new(0)) { |c, h| h[c] += 1 }
letters2 = string2.chars.each_with_object(Hash.new(0)) { |c, h| h[c] -= 1 }
diff = letters1.merge(letters2) { |key, count1, count2| count1 + count2 }
return diff.values.select { |v| v != 0 } .sort == [-1, 1]
end

mapping elements of an array differently based on position

I want to map elements of an array such that all elements
of the array are floats, except the first element which
is a string.
Anyone know how I can do this?
Tried this but doesn't work:
arr = arr.map { |e| e.to_i if e != arr.first }
Another solution is
[array.first] + array.drop(1).map &:to_f
This makes it clear that you want the first element separate from the rest, and you want the rest of the elements to be of type Float. Other options include
array.map { |element, index| index == 0 ? element : element.to_f }
array.map { |element| element == array.first ? element : element.to_f }
You can use a short ternary expression here:
a.map { |e| ( e == a.first ) ? e : e.to_f }
Another option (if you don't want to use ternary operators) is to do the following:
arr = arr.map { |e| (e == arr.first) && e || e.to_f}
This alternative is discussed here. A limitation with this method is that the first element in the array cannot be nil (or some other value that would evaluate false in a boolean evaluation), because if so, it will evaluate to the || expression and return e.to_f instead of just e.
Ruby 1.9 only?
arr = arr.map.with_index { |e, i| i.zero? ? e.to_s : e.to_f }
You can ask the objects themselves whether they're numbers.
"column heading".respond_to?(:to_int) # => false
3.1415926.respond_to?(:to_int) # => true
new_arr = arr.map do |string_or_float|
if string_or_float.respond_to?(:to_int)
string_or_float.to_int # Change from a float into an integer
else
string_or_float # Leave the string as-is
end
end
respond_to?(:to_int) means "Can I call to_int on you?"
to_int is a method that only objects that are readily convertable to integers should have. Unlike to_i, which is "I'm not very much like an integer, but you can try to convert me into a integer", to_int means "I'm very much like an integer - convert me into an integer with full confidence!"

Nested hash defined?() [duplicate]

This question already has answers here:
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
(16 answers)
Closed 7 years ago.
What's the most concise way to determine if #hash[:key1][:key2] is defined, that does not throw an error if #hash or #hash[:key1] are nil?
defined?(#hash[:key1][:key2]) returns True if #hash[:key1] exists (it does not determine whether :key2 is defined)
When using ActiveSupport (Rails) or Backports, you can use try:
#hash[:key1].try(:fetch, :key2)
You could even handle #hash being nil:
#hash.try(:fetch, :key1).try(:fetch, :key2)
If you want #hash to always return a hash for a missing key:
#hash = Hash.new { |h,k| h[k] = {} }
#hash[:foo] # => {}
You could also define this recursive:
def recursive_hash
Hash.new { |h,k| h[k] = recursive_hash }
end
#hash = recursive_hash
#hash[:foo][:bar][:blah] = 10
#hash # => {:foo => {:bar => {:blah => 10}}}
But to answer your question:
module HasNestedKey
Hash.send(:include, self)
def has_nested_key?(*args)
return false unless sub = self[args.shift]
return true if args.empty?
sub.respond_to?(:has_nested_key?) and sub.has_nested_key?(*args)
end
end
#hash.has_nested_key? :key1, :key2
Perhaps I am missing something, but if all you care about is concise...why not:
#hash && #hash[:key1] && #hash[:key1][:key2]
or if you want to save a few characters
#hash && (h = #hash[:key1]) && h[:key2]
if any part of this fails, it returns nil otherwise it returns the value associated with :key2 or true.
The reason the defined? returns true even if :key2 is not there is because it just checks whether the object you are referencing exists, which in that case is the method [] which is an alias for the method fetch which does exist on the hash #hash[:key1] but if that were to return nil, there is no fetch method on nil and it would return nil. That being said, if you had to go n deep into an embedded hash, at some point it would become more efficient to call:
defined?(#hash[:key1][:key2][:key3]) && #hash[:key1][:key2][:key3]
Using Hash#fetch
You can use the Hash#fetch method with a default of {} so that it is safe to call has_key? even if the first level key doesn't exist. e.g.
!hash.nil? && hash.fetch(key1, {}).has_key?(key2)
Alternative
Alternatively you can use the conditional operator e.g.
!hash.nil? && (hash.has_key?(key1) ? hash[key1].has_key?(key2) : false)
i.e. if hash doesn't have key key1 then just return false without looking for the second level key. If it does have key1 then return the result of checking key1's value for key2.
Also, if you want to check that hash[key1]'s value has a has_key? method before calling it:
!hash.nil? && (hash.has_key?(key1) ? hash[key1].respond_to?(:has_key?) &&
hash[key1].has_key?(key2) : false)
#hash[:key1].has_key? :key2
If you don't care about distinguishing nonexistent #hash[:key1][:key2] (at any of 3 levels) from #hash[:key1][:key2] == nil, this is quite clean and works for any depth:
[:key1,:key2].inject(hash){|h,k| h && h[k]}
If you want nil to be treated as existing, use this instead:
(hash[:key1].has_key?(:key2) rescue false)
Another option, one that I just discovered, is to extend Hash with a seek method. Technique comes from Corey O'Daniel.
Stick this in an initializer:
class Hash
def seek(*_keys_)
last_level = self
sought_value = nil
_keys_.each_with_index do |_key_, _idx_|
if last_level.is_a?(Hash) && last_level.has_key?(_key_)
if _idx_ + 1 == _keys_.length
sought_value = last_level[_key_]
else
last_level = last_level[_key_]
end
else
break
end
end
sought_value
end
end
Then just call:
#key_i_need = #hash.seek :one, :two, :three
You'll get the value, or nil if it doesn't exist.

How do I test if all items in an array are identical?

I can generate a few lines of code that will do this but I'm wondering if there's a nice clean Rubyesque way of doing this. In case I haven't been clear, what I'm looking for is an array method that will return true if given (say) [3,3,3,3,3] or ["rabbits","rabbits","rabbits"] but will return false with [1,2,3,4,5] or ["rabbits","rabbits","hares"].
Thanks
You can use Enumerable#all? which returns true if the given block returns true for all the elements in the collection.
array.all? {|x| x == array[0]}
(If the array is empty, the block is never called, so doing array[0] is safe.)
class Array
def same_values?
self.uniq.length == 1
end
end
[1, 1, 1, 1].same_values?
[1, 2, 3, 4].same_values?
What about this one? It returns false for an empty array though, you can change it to <= 1 and it will return true in that case. Depending on what you need.
I too like preferred answer best, short and sweet. If all elements were from the same Enumerable class, such as Numeric or String, one could use
def all_equal?(array) array.max == array.min end
I would use:
array = ["rabbits","rabbits","hares", nil, nil]
array.uniq.compact.length == 1
I used to use:
array.reduce { |x,y| x == y ? x : nil }
It may fail when array contains nil.

Resources