Deleting a modified object from a set in a no-op? - ruby

See the example below
require "set"
s = [[1, 2], [3, 4]].to_set # s = {[1, 2], [3, 4]}
m = s.max_by {|a| a[0]} # m = [3, 4]
m[0] = 9 # m = [9, 4], s = {[1, 2], [9, 4]}
s.delete(m) # s = {[1, 2], [9, 4]} ?????
This behaves differently from an array. (If we remove .to_set, we will get s = [[1, 2]] which is expected.) Is this a bug?

Yes, this is a bug or at least I'd call it a bug. Some would call this "an implementation detail accidentally leaking to the outside world" but that's just fancy pants city-boy talk for bug.
The problem has two main causes:
You're modifying elements of the Set without Set knowing about it.
The standard Ruby Set is implemented as a Hash.
The result is that you're modifying the internal Hash's keys without the Hash knowing about it and that confuses the poor Hash into not really knowing what keys it has anymore. The Hash class has a rehash method:
rehash → hsh
Rebuilds the hash based on the current hash values for each key. If values of key objects have changed since they were inserted, this method will reindex hsh.
a = [ "a", "b" ]
c = [ "c", "d" ]
h = { a => 100, c => 300 }
h[a] #=> 100
a[0] = "z"
h[a] #=> nil
h.rehash #=> {["z", "b"]=>100, ["c", "d"]=>300}
h[a] #=> 100
Notice the interesting behavior in the example included with the rehash documentation. Hashes keep track of things using the k.hash values for the key k. If you have an array as a key and you change the array, you can change the array's hash value as well; the result is that the Hash still has that array as a key but the Hash won't be able to find that array as a key because it will be looking in the bucket for the new hash value but the array will be in the bucket for the old hash value. But, if you rehash the Hash, it will all of a sudden be able to find all of its keys again and the senility goes away. Similar problems will occur with non-array keys: you just have to change the key in such a way that its hash value changes and the Hash containing that key will get confused and wander around lost until you rehash it.
The Set class uses a Hash internally to store its members and the members are used as the hash's keys. So, if you change a member, the Set will get confused. If Set had a rehash method then you could kludge around the problem by slapping the Set upside the head with rehash to knock some sense into it; alas, there is no such method in Set. However, you can monkey patch your own in:
class Set
def rehash
#hash.rehash
end
end
Then you can change the keys, call rehash on the Set, and your delete (and various other methods such as member?) will work properly.

Related

Ruby how to return an element of a dictionary?

# dictionary = {"cat"=>"Sam"}
This a return a key
#dictionary.key(x)
This returns a value
#dictionary[x]
How do I return the entire element
"cat"=>"Sam"
#dictionary
should do the trick for you
whatever is the last evaluated expression in ruby is the return value of a method.
If you want to return the hash as a whole. the last line of the method should look like the line I have written above
Your example is a bit (?) misleading in a sense it only has one pair (while not necessarily), and you want to get one pair. What you call a "dictionary" is actually a hashmap (called a hash among Rubyists).
A hashrocket (=>) is a part of hash definition syntax. It can't be used outside it. That is, you can't get just one pair without constructing a new hash. So, a new such pair would look as: { key => value }.
So in order to do that, you'll need a key and a value in context of your code somewhere. And you've specified ways to get both if you have one. If you only have a value, then:
{ #dictionary.key(x) => x }
...and if just a key, then:
{ x => #dictionary[x] }
...but there is no practical need for this. If you want to process each pair in a hash, use an iterator to feed each pair into some code as an argument list:
#dictionary.each do |key, value|
# do stuff with key and value
end
This way a block of code will get each pair in a hash once.
If you want to get not a hash, but pairs of elements it's constructed of, you can convert your hash to an array:
#dictionary.to_a
# => [["cat", "Sam"]]
# Note the double braces! And see below.
# Let's say we have this:
#dictionary2 = { 1 => 2, 3 => 4}
#dictionary2[1]
# => 2
#dictionary2.to_a
# => [[1, 2], [3, 4]]
# Now double braces make sense, huh?
It returns an array of pairs (which are arrays as well) of all elements (keys and values) that your hashmap contains.
If you wish to return one element of a hash h, you will need to specify the key to identify the element. As the value for key k is h[k], the key-value pair, expressed as an array, is [k, h[k]]. If you wish to make that a hash with a single element, use Hash[[[k, h[k]]]].
For example, if
h = { "cat"=>"Sam", "dog"=>"Diva" }
and you only wanted to the element with key "cat", that would be
["cat", h["cat"]] #=> ["cat", "Sam"]
or
Hash[[["cat", h["cat"]]]] #=> {"cat"=>"Sam"}
With Ruby 2.1 you could alternatively get the hash like this:
[["cat", h["cat"]]].to_h #=> {"cat"=>"Sam"}
Let's look at a little more interesting case. Suppose you have an array arr containing some or all of the keys of a hash h. Then you can get all the key-value pairs for those keys by using the methods Enumerable#zip and Hash#values_at:
arr.zip(arr.values_at(*arr))
Suppose, for example,
h = { "cat"=>"Sam", "dog"=>"Diva", "pig"=>"Petunia", "owl"=>"Einstein" }
and
arr = ["dog", "owl"]
Then:
arr.zip(h.values_at(*arr))
#=> [["dog", "Diva"], ["owl", "Einstein"]]
In steps:
a = h.values_at(*arr)
#=> h.values_at(*["dog", "owl"])
#=> h.values_at("dog", "owl")
#=> ["Diva", "Einstein"]
arr.zip(a)
#=> [["dog", "Diva"], ["owl", "Einstein"]]
To instead express as a hash:
Hash[arr.zip(h.values_at(*arr))]
#=> {"dog"=>"Diva", "owl"=>"Einstein"}
You can get the key and value in one go - resulting in an array:
#h = {"cat"=>"Sam", "dog"=>"Phil"}
key, value = p h.assoc("cat") # => ["cat", "Sam"]
Use rassoc to search by value ( .rassoc("Sam") )

Why does an element of an array assigned to a variable changes when the variable is changed?

I have a two-dimensional array
test_array = [[4,3],[4,5],[6,7]]
I want to assign the last element of it to a variable
test = test_array.last #=> [6, 7]
And I want to change it
test[0] += 1
test #=> [7, 7]
Why has the last element of the array changed as well?
test_array #=> [[4, 3], [4, 5], [7, 7]]
How can I avoid this?
The variable test holds a reference to the array test_array.last. If you modified the value of test, the value of test_array.last is modified as well.
Your multi-dimensional array (test_array) has a reference to the last element, so any changes you make to test will be seen in the test_array. If you don't desire this behavior, duplicate the last element before modifying it:
test_array = [[4,3],[4,5],[6,7]]
test = test_array.last.dup
# => [6, 7]
test[0] += 1
test
# => [7, 7]
test_array
# => [[4, 3], [4, 5], [6, 7]]
Here's how you can check to see what's happening; It's a technique I use periodically when I'm helping debug someone else's code and need to 'splain what is happening:
foo = [[1,2]]
bar = foo.first
bar contains a pointer AKA reference to the sub-array in foo. That means that the array at foo.first and bar are pointing to the same variable space in memory:
foo.first.object_id # => 70357558266700
bar.object_id # => 70357558266700
Because they're the same variable space, changing the array that bar points to, or changing the one that foo.first points to, will change the other one.
That can be useful if you understand what's going on, because if you have a big array and want to temporarily point to a deeply-nested element in it, you can assign a variable to point to it rather than use a long array accessor.
Because that is the very array that you changed. There is no way to avoid that. You cannot change an array without changing it.

getting differences between values in an array

I want to write an Array method in ruby that takes the successive values in the array and returns their differences as a new array (unshifting a '0' in at the beginning).
So feeding the array [4,7,11,16] into the method returns a new array [4,3,4,5].
1) does such a method already exist?
If not, then I think I know how to write it. However,
2) does a method already exist which allows me to test the input array and make sure it only consists of integers and/or floats?
Again, if not, I think I know how to write one.
p [4,7,11,16].unshift(0).each_cons(2).map{|a,b| b-a} # => [4, 3, 4, 5]
Keep it simple:
arr = [4,7,11,16]
last = 0
arr.map { |e| new=e-last; last=e; new }
#=> [4, 3, 4, 5]
Another way:
a = [arr.first]
enum = arr.each
loop do
a << -enum.next + enum.peek
end
a
#=> [4, 3, 4, 5]
Enumerator#peek raises a StopIteration exception when enum is at its last element. Kernel#loop handles the exception by breaking from the loop.
Regarding the first method, I am not aware of any such method in the Ruby Array class.
Regarding the second one, you can do it as explained in this answer:
your_array.all? {|i| i.is_a? Numeric }

How to iterate over part of a hash in Ruby?

h = Hash.new
(1..100).each { |v| h.store(v * 2, v*v) }
What is the best way to iterate over a given part of the hash without using the keys? For example, from element 10 to element 20? Using Ruby 1.9.3.
EDIT - In response to Dave's comment:
Originally I wanted to access the data through keys (hence the hash). But I also want to iterate by element number. BTW, each element is a hash.
So, what is the best way to design a hash of hashes or array of hashes that can be iterated by element number or accessed by key? The data looks like the following. There are missing dates.
6/23/2011 -> 5, 6, 8, 3, 6
6/26/2011 -> 6, 8, 4, 8, 5
6/27/2011 -> 8, 4, 3, 2, 7
If I understand what you're asking for, you can iterate over a portion of your hash as follows. This gives you the 1001st through 2000th values:
h.keys[1000..1999].each do |key|
# Do something with h[key]
end
I think you better use Array for that (Hash in Ruby 1.9.3 are ordered but the access method is the keys). So:
a = h.values
# or
a = h.to_a
Convert it to an array, then slice it:
h.to_a[10..20].each { |k, v| do_stuff }
Note that before Ruby 1.9, the order of elements in a hash are not guaranteed, so this will not necessarily work as you expect.
Alternatively, you could use each_with_index and skip over the unwanted elements:
h.each_with_index do |(k, v), i|
next unless (10..20).include?(i)
# do stuff
end
h = Hash.new
(1..100).each { |v| h.store(v * 2, v*v) }
#for an array of arrays
h.drop(9).take(10) #plus an optional block
#if the slice must be a hash:
slice = Hash[h.drop(9).take(10)]
But if this is an often repeating operation you might be better off using a database.

Ruby: Properties of a hash key

I will just paste down a simple example i tried so that it would be clear to those who read this.
irb(main):001:0> h = { }
=> {}
irb(main):002:0> a=[1,2,3]
=> [1, 2, 3]
irb(main):003:0> a.object_id
=> 69922343540500
irb(main):004:0> h[a] = 12 #Hash with the array as a key
=> 12
irb(main):005:0> a << 4 #Modified the array
=> [1, 2, 3, 4]
irb(main):006:0> a.object_id #Object id obviously remains the same.
=> 69922343540500
irb(main):007:0> h[a] #Hash with the same object_id now returns nil.
=> nil
irb(main):008:0> h #Modified hash
=> {[1, 2, 3, 4]=>12}
irb(main):009:0> h[[1,2,3,4]] #Tried to access the value with the modified key -
=> nil
irb(main):011:0> h.each { |key,value| puts "#{key.inspect} maps #{value}" }
[1, 2, 3, 4] maps 12
=> {[1, 2, 3, 4]=>12}
Now when i iterate over the hash, its possible to identify the map between the key and the value.
Could some one please explain me this behaviour of ruby hash and what are the properties of hash keys.
1) As i mentioned above, the object_id hasn't changed - then why is the value set to nil.
2) Is there any possible way so that i can get back the value '12' from the hash 'h' because h[[1,2,3,4]] as mentioned above returns nil.
This happens because the key should not have its value changed while it is in use. If the value changes, we should rebuild the hash, based on its current value. Look at Ruby API for rehash method. You can get back the value, by rebuilding the hash again after the key is changed, like this:
irb(main):022:0> h.rehash
=> {[1, 2, 3, 4]=>12}
irb(main):023:0> h[a]
=> 12
Hash keys are checked using the #eql? method, and since [1, 2, 3] isn't .eql? to [1, 2, 3,4] your hash lookup has a different result.
Maybe you want to be using something other than an Array as your Hash key if the semantics aren't working for you?
The ruby hash api provides the answer: the key should not have its value changed while it is in use as a key.
I guess intern a hash is calculated for a and used for quick lookup (because a key shouldn't change, the hash is always the same). So when you do h[a] it doesn't find a match ([1,2,3].hash != [1,2,3,4].hash) and when you do h[[1,2,3]] the hashes match but the object doesn't match ([1,2,3] != [1,2,3,4]).
A fix is to use the object_id as key because it doesn't change, h[a.object_id] = 12 will return 12 when a changes. Ofcourse this has the downside that h[[1,2,3].object_id] won't return 12.
Stefaan Colman's answer is more thorough, but a couple of observations:
Ruby uses the Object#hash method to hash objects.
You can get 12 back out like by doing a.delete(4); h[a] at that point, [1,2,3] is also able to be used as a key again.

Resources