How do `Hash#reject!` and `Hash#reject` differ from `Hash#delete_if`? - ruby

How do reject! and reject differ from delete_if for a Hash in Ruby? Can anyone explain the differences between them with simple code snippets?

Since the other answers are referring to Array#delete_if and not Hash#delete_if, which seems to be what you are asking, I thought I should clarify.
As others have pointed out, reject and reject! differ in that reject! version modifies the hash in-place, while reject creates a new hash. Meanwhile delete_if is almost the same as reject!.
In fact, for an Array, reject! and delete_if are exactly the same.
However, for a Hash, they are slightly different. reject! returns nil if no changes were made, or the hash if changes were made. delete_if always returns the hash.
hash = {a: 1, b: 2, c: 3}
return_value = hash.delete_if {|k, v| v > 100}
# hash is unchanged, return_value is {a: 1, b: 2, c: 3}
return_value = hash.reject! {|k, v| v > 100}
# hash is unchanged, return_value is nil
So if you wanted to check whether changes were made to the hash after deleting the elements, you could use reject! and check the return value.

If you read the docs it tells you that reject! is "Equivalent to Array#delete_if"
reject and reject! differ in that the bang (reject!) causes the changes to happen directly on the array you're working with, whereas reject will leave the array you're working with untouched, but will return a new array.
a = [ "a", "b", "c" ]
b = a.reject {|x| x >= "b" } #=> a is untouched, but b is ["a"]
a.reject! {|x| x >= "b" } #=> a is now modified and is ["a"]

I suppose you could read this from the docs:
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-reject-21 :
Equivalent to Array#delete_if, deleting elements from self for which the block evaluates to true, but returns nil if no changes were made. The array is changed instantly every time the block is called and not after the iteration is over. See also Enumerable#reject and Array#delete_if.
If no block is given, an enumerator is returned instead.
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-reject:
Returns a new array containing the items in self for which the block is not true. See also Array#delete_if
If no block is given, an enumerator is returned instead.

Related

Ruby select method selecting values that do not meet criteria

Have the following code which should select every other character of a string and make a new string out of them:
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.join
end
However, select returns this output with test case "hello":
"h0l2o4"
When using map instead I get the desired result:
"hlo"
Is there a reason why select would not work in this case? In what scenarios would it be better to use map over select and vice versa
If you still want to use select, try this.
irb(main):005:0> "hello".chars.select.with_index {|m, index| m if index % 2 == 0}.join
=> "hlo"
each_with_index does not work because it is selecting both the character and the index and then joining all of that.
The reason that select does not work in this case is that select "Returns an array containing all elements of enum for which the given block returns a true value" (see the doc here), so what you get in your case is an array of arrays [['h',0],['l',2],['o',4]] which you then join to get "h0l2o4".
So select returns a subset of an enumerable. map returns a one to one mapping of the provided enumerable. For example the following would "fix" your problem by using map to extract character from each value returned by select.
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.map { |pair| pair.first }.join
end
puts(bits "hello")
=> hlo
For lots of reasons this is not a good way to get every other character from a string however.
Here is another example using map. In this case each index is mapped to either the character or nil then joined.
def bits(string)
string.chars.each_index.map {|i| string[i] if i.even? }.join
end
If you use Enumerable#map, you will return an array having one element for each character in the string.
arr = "try this".each_char.map.with_index { |c,i| i.even? ? c : nil }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
which is the same as
arr = "try this".each_char.map.with_index { |c,i| c if i.even? }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
My initial answer suggested using Array#compact to remove the nils before joining:
arr.compact.join
#=> "tyti"
but as #npn notes, compact is not necessary because Array#join applies NilClass.to_s to the nil's, converting them to empty strings. Ergo, you may simply write
arr.join
#=> "tyti"
Another way you could use map is to first apply Enumerable#each_cons to pass pairs of characters and then return the first character of each pair:
"try this".each_char.each_cons(2).map(&:first).join
#=> "tyti"
Even so, Array#select is preferable, as it returns only the characters of interest:
"try this".each_char.select.with_index { |c,i| i.even? }.join
#=> "tyti"
A variant of this is:
even = [true, false].cycle
#=> #<Enumerator: [true, false]:cycle>
"try this".each_char.select { |c| even.next }.join
#=> "tyti"
which uses Array#cycle to create the enumerator and Enumerator#next to generate its elements.
One small thing: String#each_char is more memory-efficient than String#chars, as the former returns an enumerator whereas the latter creates a temporary array.
In general, when the receiver is an array,
use map when you want to return an array containing one element for each element of the receiver.
use Enumerable#find when you want to return just one element of the receiver.
use Array#select or Array#reject (or Enumerable#select or Enumerable#reject if the receiver is an enumerator).
Me, I'd use a simple regular expression:
"Now is the time to have fun.".scan(/(.)./).join
#=> "Nwi h iet aefn"

Detecting if a key-value pair exists within a hash

I cannot find a way to determine if a key-value pair exists in a hash.
h4 = { "a" => 1, "d" => 2, "f" => 35 }
I can use Hash#has_value? and Hash#has_key? to find information about a key or a value individually, but how can I check if a pair exists?
Psuedo-code of what I'm after:
if h4.contains_pair?("a", 1)
Just use this:
h4['a'] == 1
It seems excessive to me, but you could monkey-patch Hash with a method like so:
class Hash
def contains_pair?(key, value)
key?(key) && self[key] == value
end
end
I confess to starting down a road and then wondering where it might take me. This may not be the best way of determining if a key/value pair is present in a hash (how could one improve on #Jordan's answer?), but I learned something along the way.
Code
def pair_present?(h,k,v)
Enumerable.instance_method(:include?).bind(h).call([k,v])
end
Examples
h = { "a"=>1, "d"=>2, "f"=>35 }
pair_present?(h,'a',1)
#=> true
pair_present?(h,'f',36)
#=> false
pair_present?(h,'hippopotamus',2)
#=> false
Discussion
We could of course convert the hash to an array and then apply Array#include?:
h.to_a.include?(['a', 1])
#=> true
but that has the downside of creating a temporary array. It would be nice if the class Hash had such an instance method, but it does not. One might think Hash#include? might be used for that, but it just takes one argument, a key.1.
The method Enumerable#include? does what we want, and of course Hash includes the Enumerable module. We can invoke that method in various ways.
Bind Enumerable#include? to the hash and call it
This was of course my answer:
Enumerable.instance_method(:include?).bind(h).call([k,v])
Use the method Method#super_method, which was introduced in v2.2.
h.method(:include?).super_method.call(['a',1])
#=> true
h.method(:include?).super_method.call(['a',2])
#=> false
Note that:
h.method(:include?).super_method
#=> #<Method: Object(Enumerable)#include?>
Do the alias_method/remove_method merry-go-round
Hash.send(:alias_method, :temp, :include?)
Hash.send(:remove_method, :include?)
h.include?(['a',1])
#=> true
h.include?(['a',2])
#=> false
Hash.send(:alias_method, :include?, :temp)
Hash.send(:remove_method, :temp)
Convert the hash to an enumerator and invoke Enumerable#include?
h.to_enum.include?(['a',1])
#=> true
h.to_enum.include?(['a',2])
#=> false
This works because the class Enumerator also includes Enumerable.
1 Hash#include? is the same as both Hash#key? and Hash#has_key?. It makes me wonder why include? isn't used for the present purpose, since determining if a hash has a given key is well-covered.
How about using Enumerable any?
h4 = { "a" => 1, "d" => 2, "f" => 35 }
h4.any? {|k,v| k == 'a' && v == 1 }

How to merge old hash with new hash, that values should be null when keys not in new hash

Assuming I got a hash of old properties from an object:
old = {a: 1, b: 2, c: nil}
Then I got a hash of new properties from user input (all keys must exists in old hash):
new = {a: 2}
Finally I want get a hash of properties to update to the object like this:
upd = {a: 2, b: nil, c: nil}
# upd = {a: 2, b: nil} would be better
Now I use map to implement this:
upd = Hash[old.map{|x| [ x[0],new[x[0]] ] } ]
I tried merge but couldn't get what I want:
old.merge(new){|k,old,new| new} #=> {:a=>2, :b=>2, :c=>nil}
But I think there must be some better way of doing this. Any help will be appreciated.
This is one way:
old.merge(old) { |k,_,_| new[k] }
#=> {a: 2, b: nil, c: nil}
This uses the form of the method Hash#merge that takes a block. The purpose of the block is to resolve values for keys that are present in both hashes involved in the merge.
Here I am merging old with itself, so the value for every key in old is determined by the block.
An array [key, old_value, new_value] is passed to the block. In this case old_value and new_value are the same, but as I will not be using that value in the block, I have replaced the two corresponding block variables with the placeholder _.
In the block I compute the value for key k to be new[k], which is nil if new does not have a key k.
This approach could also be implemented with Enumerable#reduce (a.k.a inject):
old.reduce(old) { |h,(k,_)| h[k] = new[k]; h }
I don't think there's an out-of-the-box way to do what you want, so your current solution seems OK.
You could avoid the [0], by doing either:
upd = Hash[old.map{|k, v| [k, new[k]] }]
or
upd = Hash[old.keys.map{|k| [k, new[k]] }]
One caveat eith the current solution: if a key exists in new, but does not exist in old, it will not be present in upd. You may be aware of this, but I thought it should be pointed out.
old.inject(new.dup) { |h, (k,v)| h[k] = nil unless (v.nil? || new.has_key?(k)); h }
# => {a: 2, b: nil}
Basically, it says to start with a copy of new. Then build up the resulting hash h by iterating over the (k,v) pairs in old.
The only extra things we want in addition to what's in new is when there was a key in old with a non-nil value, but now the key is not present in new and so we want to indicate that by having the key in the result, but with a nil value. So if (k,v) is a par in old, and h is the result we're building, then we want to add h[k] = nil if v was not nil, but k wasn't even a key in new. Since there's a bunch of negations in there, it's simpler to express it with an unless, resulting in the form above.

How to remove elements of array in place returning the removed elements

I have an array arr. I want to destructively remove elements from arr based on a condition, returning the removed elements.
arr = [1,2,3]
arr.some_method{|a| a > 1} #=> [2, 3]
arr #=> [1]
My first try was reject!:
arr = [1,2,3]
arr.reject!{|a| a > 1}
but the returning blocks and arr's value are both [1].
I could write a custom function, but I think there is an explicit method for this. What would that be?
Update after the question was answered:
partition method turns out to be useful for implementing this behavior for hash as well. How can I remove elements of a hash, returning the removed elements and the modified hash?
hash = {:x => 1, :y => 2, :z => 3}
comp_hash, hash = hash.partition{|k,v| v > 1}.map{|a| Hash[a]}
comp_hash #=> {:y=>2, :z=>3}
hash #=> {:x=>1}
I'd use partition here. It doesn't modify self inplace, but returns two new arrays. By assigning the second array to arr again, it gets the results you want:
comp_arr, arr = arr.partition { |a| a > 1 }
See the documentation of partition.
All methods with a trailing bang ! modify the receiver and it seems to be a convention that these methods return the resulting object because the non-bang do so.
What you can to do though is something like this:
b = (arr.dup - arr.reject!{|a| a>1 })
b # => [2,3]
arr #=> [1]
Here is a link to a ruby styleguide which has a section on nameing - although its rather short
To remove (in place) elements of array returning the removed elements one could use delete method, as per Array class documentation:
a = [ "a", "b", "b", "b", "c" ]
a.delete("b") #=> "b"
a #=> ["a", "c"]
a.delete("z") #=> nil
a.delete("z") { "not found" } #=> "not found"
It accepts block so custom behavior could be added, as needed

Why does Array#each return an array with the same elements?

I'm learning the details of how each works in ruby, and I tried out the following line of code:
p [1,2,3,4,5].each { |element| el }
And the result is an array of
[1,2,3,4,5]
But I don't think I fully understand why. Why is the return value of each the same array? Doesn't each just provide a method for iterating? Or is it just common practice for the each method to return the original value?
Array#each returns the [array] object it was invoked upon: the result of the block is discarded. Thus if there are no icky side-effects to the original array then nothing will have changed.
Perhaps you mean to use map?
p [1,2,3,4,5].map { |i| i*i }
If you want, for some reason, to suppress the output (for example debugging in console) here is how you can achive that
[1,2,3,4,5].each do |nr|
puts nr.inspect
end;nil
Array#each
The block form of Array#each returns the original Array object. You generally use #each when you want to do something with each element of an array inside the block. For example:
[1, 2, 3, 4, 5].each { |element| puts element }
This will print out each element, but returns the original array. You can verify this with:
array = [1, 2, 3, 4, 5]
array.each { |element| element }.object_id === array.object_id
=> true
Array#map
If you want to return a new array, you want to use Array#map or one of its synonyms. The block form of #map returns a different Array object. For example:
array.object_id
=> 25659920
array.map { |element| element }.object_id
=> 20546920
array.map { |element| element }.object_id === array.object_id
=> false
You will generally want to use #map when you want to operate on a modified version of the original array, while leaving the original unchanged.
All methods return something. Even if it's just a nil object, it returns something.
It may as well return the original object rather than return nil.

Resources