Find difference between arrays in Ruby, where the elements are hashes - ruby

I have two arrays of hashes I would like to find the difference between. My issue is the array elements are single item hashes.
So far, using array1 - array2 appears to be working correctly but do I need to watch out for gotchas here? The hash elements themselves read like h = {'ID' => '76322'}, where the numeric value differs from hash to hash, so nothing too fancy.
[EDIT]
Here's what I'm looking for:
array1 = []
array2 = []
h = {'ID' => '76322'}
array1.push(h)
h = {'ID' => '7891'}
array1.push(h)
array2.push(h)
array1 = array1 - array2 # should result in array1 having a single hash of values {'ID', '76322'}

array1 - array2 works by putting the elements of array2 into a temporary hash, then returning all elements of array1 that don't appear in the hash. Hash elements are compared using == to determine whether they match.
Comparing two hashes with == gives true if all the keys and values of the hashes match using ==. So
h1 = {'ID' => '7891'}
h2 = {'ID' => '7891'}
h1 == h2
evaluates to true, even though h1 and h2 are different hashes, and the corresponding elements will be correctly removed.
The only consideration I can think of is if you always have strings everywhere in the hash keys and values. If they are sometimes integers, or symbols, like {:ID => 7891} then you aren't going to get the results you want, because :ID == 'ID' and '7891' == 7891 are both false.

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") )

A way to select maximal values from an array

This will return the first instance of the longest string in an array:
["abcd","efgh","ijk"].max_by { |x| x.length } # => "abcd"
Similarly to this, is there a nice way to get an array of all strings with the maximal length?
["abcd","efgh","ijk"].some_trick ... # => ["abcd","efgh"]
Here we go :
["abcd","efgh","ijk"].group_by(&:size).max.last #=> ["abcd","efgh"]
Explanation :
Enumerable#group_by gives a hash containing each unique size of strings contained in the array as keys, and the matching strings as values => {4=>["abcd", "efgh"], 3=>["ijk"]}
Enumerable#max applied on a Hash will give us the highest key with its matching values, in an Array like this : [key, values] => [4, ["abcd", "efgh"]]
Array#last will give us the last element of the array ... => ["abcd", "efgh"]

Ruby koans's assertion on test_hash_is_unordered

In Ruby 1.9 a Hash is sorted on the basis of order of insertion.
Why the Ruby koans's assertion on test_hash_is_unordered method returns true?
To me, the method's title is quite misleading... maybe it refers to the fact that Ruby will recognize 2 equal hashes that were created with different keys order insetions.
But, theorically, this kind of assertion:
hash1 = { :one => "uno", :two => "dos" }
hash2 = { :two => "dos", :one => "uno" }
assert_equal ___, hash1 == hash2
Should return false. Or not?
From the fine manual:
hsh == other_hash → true or false
Equality—Two hashes are equal if they each contain the same number of keys and if each key-value pair is equal to (according to Object#==) the corresponding elements in the other hash.
So two Hashes are considered equal if they have the same key/value pairs regardless of order.
The examples in the documentation even contain this:
h2 = { 7 => 35, "c" => 2, "a" => 1 }
h3 = { "a" => 1, "c" => 2, 7 => 35 }
h2 == h3 #=> true
Yes, the test_hash_is_unordered title is somewhat misleading as order isn't specifically being testing, only order with respect to equality is being demonstrated.
I think it's just a question of what 'unordered' means in such a context.
As a human, I would find it very difficult to compare two sets if they were not in order. The problem is that I can not easily match up identical elements and see if the sets are equivalent. Unless the sets happened to be listed in the same order, I would see them as unequal. This seems to be the conclusion that you came to also.
However, the thing is that the order of items in a mathematical concept of a set is simply unimportant. There is no way to 'order' the items, so two sets are identical if they contain the same elements. The set items are unordered, but they are are not 'out of order'; the concept of order does not apply.
I suppose that this is encapsulated entirely in the expression 'hash_is_unordered' but that was not immediately obvious to me, at least!

merging arrays of hashes

I have two arrays, each holding arrays with attribute hashes.
Array1 => [[{attribute_1 = A}, {attribute_2 = B}], [{attribute_1 = A}, {attribute_4 = B}]]
Array2 => [{attribute_3 = C}, {attribute_2 = D}], [{attribute_3 = C, attribute_4 = D}]]
Each array in the array is holding attribute hashes for an object. In the above example, there are two objects that I'm working with. There are two attributes in each array for each of the two objects.
How do I merge the two arrays? I am trying to get a single array of 'object' arrays (there is no way to get a single array from the start because I have to make two different API calls to get these attributes).
DesiredArray => [[{attribute_1 = A, attribute_2 = B, attribute_3 = C, attribute_4 = D}],
[{attribute_1 = A, attribute_2 = B, attribute_3 = C, attribute_4 = D}]]
I've tried a couple things, including the iteration methods and the merge method, but I've been unable to get the array I need.
You seem to have parallel arrays of hashes. We can use zip to turn the parallel arrays into a single array of arrays of hashes. We can then map each array of hashes into a single hash using inject and merge:
#!/usr/bin/ruby1.8
require 'pp'
array1 = [{:attribute_1 => :A, :attribute_2 => :B}, {:attribute_1 => :A, :attribute_4 => :B}]
array2 = [{:attribute_3 => :C, :attribute_2 => :D}, {:attribute_3 => :C, :attribute_4 => :D}]
pp array1.zip(array2).collect { |array| array.inject(&:merge) }
# => [{:attribute_2=>:D, :attribute_1=>:A, :attribute_3=>:C},
# => {:attribute_4=>:D, :attribute_1=>:A, :attribute_3=>:C}]
I don't think my answer is valid anymore, since the question has been edited later.
Here, first I am fixing your array and hash notation in your question.
Array1 = [{'attribute_1' => 'A', 'attribute_2' => 'B'}, {'attribute_1' => 'A', 'attribute_2' => 'B'}]
#=> [{"attribute_1"=>"A", "attribute_2"=>"B"}, {"attribute_1"=>"A", "attribute_2"=>"B"}]
Array2 = [{'attribute_3' => 'C', 'attribute_2' => 'D'}, {'attribute_3' => 'C', 'attribute_4' => 'D'}]
#=> [{"attribute_2"=>"D", "attribute_3"=>"C"}, {"attribute_3"=>"C", "attribute_4"=>"D"}]
You can simply concatenate the two arrays to get your desired array like so:
DesiredArray = Array1+Array2
# => [{"attribute_1"=>"A", "attribute_2"=>"B"}, {"attribute_1"=>"A", "attribute_2"=>"B", {"attribute_2"=>"D", "attribute_3"=>"C"}, {"attribute_3"=>"C", "attribute_4"=>"D"}]

Ruby Array Intersection, Hash.has_value? for intersection with array[i+1] based on hash values

I am trying to populate an array with an intersection of an array and hash based on hash values:
array2 = ["hi","world","One"]
VALS = {:one => "One", :two => "Two"}
array2 = VALS.values & array2
print array2
Works fine for intersection of hash values and array2[i], but if I want to instead populate with array2[i+1] element from array2, I am lost.
Also tried:
array2.select { |i| VALS.has_value? i ? array2[i+1] : nil }
But no luck.
Any ideas?
array = []
array2.each_with_index{|v,i| array << (VALS.has_value? i ? array2[i+1] : nil)}
It sounds to me like you're trying to treat your array as a series of either internal keys and values, or external keys into the inverse of vals. So the correct result from your example would be:
{"hi" => "World", "One" => :one}
Is that right? If so, here:
invertedvals = vals.invert
Hash[*array2.map {|x| [x,invertedvals[x]]}.flatten.compact]
If not, please clarify what result you're trying to produce...
Here is what I ended up with as an answer to my own question:
array1.each do |i|
puts "#"*5 + i + "#"*5 if !array2.include? i
if array2.include? i
result << i+":" << array2[array2.index(i)+1]
end
end
Is there a speedier algorithm for this? Would like to use:
array1 & array2
Also used inject at one point, benchmark showed very little difference between any of them.

Resources