How to remove duplicates from array of hashes based on keys only? - ruby

How would you go about removing duplicates based on the key?
values = [{"a"=>"1"}, {"a"=>"2"}, {"b"=>"1"}, {"a"=>"4"}]
How can I ignore the value and run uniq based on key so that it returns:
[{'a' => '1'}, {'b' => '1'}]

Assuming you don't care which value gets clobbered, just run them into a hash (which does have unique keys and is therefore probably the right collection class for this case):
h = {}
values.each{|i|i.each{|k,v|h[k] = v}}
puts h # => {"a"=>"4", "b"=>"1"}
... or if you want the first of each key:
h = {}
values.each{|i|i.each{|k,v|h[k] = v unless h[k]}}
If you want to get back to a Array:
h.each{|k,v|a << {k=>v}}

The following will work only in ruby 1.9, so it might be useless.
Hash[values.map(&:first).reverse].map{|a| Hash[*a]}
If you need it in the original order,
values & Hash[values.map(&:first).reverse].map{|a| Hash[*a]}

Without introducing any intermediate variables the following 1 liner will do the trick:
> [{"a"=>"1"}, {"a"=>"2"}, {"b"=>"1"}, {"a"=>"4"}].inject({}) {|s,h| s.merge(h) unless s.keys.include? h.keys}.inject([]) {|s,h| s << {h[0]=>h[1]}}
=> [{"a"=>"4"}, {"b"=>"1"}]

Related

How to remove duplicate pair values from given array in Ruby?

I want to remove a pair of 'duplicates' from an array of strings, where each element has the form R1,R2, with varying numbers. In my case, a duplicate would be R2,R1 because it has the same elements of R1,R2 but inverted.
Given:
a = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
The resulting array should be like so:
a = ['R1,R2', 'R3,R4', 'R5,R6']
How could I remove the duplicates so I would have the following?
A solution with Set
require 'set'
a.uniq { |item| Set.new(item.split(",")) } # => ["R1,R2", "R3,R4", "R5,R6"]
Here is a working example :
array = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
array.uniq { |a| a.split(',').sort }
try this,
def unique(array)
pure = Array.new
for i in array
flag = false
for j in pure
flag = true if (j.split(",").sort == i.split(",").sort)
end
pure << i unless flag
end
return pure
end
reference: https://www.rosettacode.org/wiki/Remove_duplicate_elements#Ruby
If the elements of your array are "pairs", they should maybe be actual pairs and not strings, like this:
pairs = [['R1', 'R2'], ['R3', 'R4'], ['R2', 'R1'], ['R5', 'R6']]
And, in fact, since order doesn't seem to matter, it looks like they really should be sets:
require 'set'
sets = [Set['R1', 'R2'], Set['R3', 'R4'], Set['R2', 'R1'], Set['R5', 'R6']]
If that is the case, then Array#uniq will simply work as expected:
sets.uniq
#=> [#<Set: {"R1", "R2"}>, #<Set: {"R3", "R4"}>, #<Set: {"R5", "R6"}>]
So, the best way would be to change the code that produces this value to return an array of two-element sets.
If that is not possible, then you should transform the value at your system boundary when it enters the system, something like this:
sets = a.map {|el| el.split(',') }.map(&Set.method(:new))

My Inner Hash is replacing the existing value when I assign a new value

I am storing the result in hash like this
I have assigned the result like this
Result['UserCreation']={"Test1"=>"Rajagopalan"}
So it created the hash like this
{"UserCreation"=>{"Test1"=>"Rajagopalan"}}
Now, I don't know how to assign another result for Test2. When I tend to assign result like this
Result['UserCreation']={"Test2"=>"Kali"}
It's replacing the existing result, and it's correctly doing it's Job, but I want to create result hash like given below when I assign the Result of Test2
{"UserCreation"=>{"Test1"=>"Rajagopalan","Test2"=>"Kali"}}
How can I achieve this?
Let us assume in this order I receive parameters
'UserCreation',{"Test1"=>"Rajagopalan"},
'UserCreation',{"Test2"=>"Kali"}
'contactcreate',{"Test2"=>"Kali"}
Result
{"UserCreation"=>{"Test1"=>"Rajagopalan","Test2"=>"Kali"},'contactcreate'=>{"Test2"=>"Kali"}}
All these values are the parameter to the functions.
You should use Hash#merge! method:
Result['UserCreation'].merge!({"Test2"=>"Kali"})
Here's a brief explanation:
When you use the assignment (Result['UserCreation']={"Test2"=>"Kali"}) you completely replace the value for the particular hash key. If you want to add (merge) something inside the existing hash you should use merge! method.
Notice that you can use Hash#merge! method because you know that the value of Result['UserCreation'] is a hash itself.
Also notice that there's merge method without bang (!). The difference that bang-version will mutate (change) your object. Consider this:
hash = {}
hash.merge({'one' => 1})
# hash variable will hold its initial value
# because `merge` method will not change it.
p hash # => {}
hash.merge!('one' => 1)
# This time we use bang-version, so hash variable
# will be updated.
p hash # => {"one"=>1}
One more thing about Ruby, notice how in the bang-version we omit curly braces. It's possible to do it if the last argument you passing to the method is a Hash.
Also, by convention in Ruby snake-case is using for variable and method naming, i.e.
result = {}
result['user_creation'] = {'test_1' => 'Rajagopalan'}
result['user_creation'].merge!('test_2' => 'Kali')
Of course, there's a field to play. For example, you can set the initial value like this:
result = {'user_creation' => {}}
result['user_creation'].merge!('test_1' => 'Rajagopalan')
result['user_creation'].merge!('test_2' => 'Kali')
or event update several pairs:
result = {'user_creation' => {}}
result['user_creation'].merge!(
'test_1' => 'Rajagopalan',
'test_2' => 'Kali'
)
UPDATE
For your case if you receive these parameters:
'UserCreation',{"Test1"=>"Rajagopalan"},
'UserCreation',{"Test2"=>"Kali"}
'contactcreate',{"Test2"=>"Kali"}
suppose that the first parameter named kind and the last one named value:
# kind = 'UserCreation' and value = '{"Test1"=>"Rajagopalan"}'.
result = {}
# Here we check `result[kind]` if there's no key, a new hash will
# be assigned, otherwise the existing value will be used.
result[kind] ||= {}
result[kind].merge!(value)
Maybe you want to use Hash#store:
result = {}
result['UserCreation'] = {"Test1"=>"Rajagopalan"}
result['UserCreation'].store("Test2", "Kali")
result #=> {"UserCreation"=>{"Test1"=>"Rajagopalan", "Test2"=>"Kali"}}

Ruby regex into array of hashes but need to drop a key/val pair

I'm trying to parse a file containing a name followed by a hierarchy path. I want to take the named regex matches, turn them into Hash keys, and store the match as a hash. Each hash will get pushed to an array (so I'll end up with an array of hashes after parsing the entire file. This part of the code is working except now I need to handle bad paths with duplicated hierarchy (top_* is always the top level). It appears that if I'm using named backreferences in Ruby I need to name all of the backreferences. I have gotten the match working in Rubular but now I have the p1 backreference in my resultant hash.
Question: What's the easiest way to not include the p1 key/value pair in the hash? My method is used in other places so we can't assume that p1 always exists. Am I stuck with dropping each key/value pair in the array after calling the s_ary_to_hash method?
NOTE: I'm keeping this question to try and solve the specific issue of ignoring certain hash keys in my method. The regex issue is now in this ticket: Ruby regex - using optional named backreferences
UPDATE: Regex issue is solved, the hier is now always stored in the named 'hier' group. The only item remaining is to figure out how to drop the 'p1' key/value if it exists prior to creating the Hash.
Example file:
name1 top_cat/mouse/dog/top_cat/mouse/dog/elephant/horse
new12 top_ab12/hat[1]/top_ab12/hat[1]/path0_top_ab12/top_ab12path1/cool
tops top_bat/car[0]
ab123 top_2/top_1/top_3/top_4/top_2/top_1/top_3/top_4/dog
Expected output:
[{:name => "name1", :hier => "top_cat/mouse/dog/elephant/horse"},
{:name => "new12", :hier => "top_ab12/hat[1]/path0_top_ab12/top_ab12path1/cool"},
{:name => "tops", :hier => "top_bat/car[0]"},
{:name => "ab123", :hier => "top_2/top_1/top_3/top_4/dog"}]
Code snippet:
def s_ary_to_hash(ary, regex)
retary = Array.new
ary.each {|x| (retary << Hash[regex.match(x).names.map{|key| key.to_sym}.zip(regex.match(x).captures)]) if regex.match(x)}
return retary
end
regex = %r{(?<name>\w+) (?<p1>[\w\/\[\]]+)?(?<hier>(\k<p1>.*)|((?<= ).*$))}
h_ary = s_ary_to_hash(File.readlines(filename), regex)
What about this regex ?
^(?<name>\S+)\s+(?<p1>top_.+?)(?:\/(?<hier>\k<p1>(?:\[.+?\])?.+))?$
Demo
http://rubular.com/r/awEP9Mz1kB
Sample code
def s_ary_to_hash(ary, regex, mappings)
retary = Array.new
for item in ary
tmp = regex.match(item)
if tmp then
hash = Hash.new
retary.push(hash)
mappings.each { |mapping|
mapping.map { |key, groups|
for group in group
if tmp[group] then
hash[key] = tmp[group]
break
end
end
}
}
end
end
return retary
end
regex = %r{^(?<name>\S+)\s+(?<p1>top_.+?)(?:\/(?<hier>\k<p1>(?:\[.+?\])?.+))?$}
h_ary = s_ary_to_hash(
File.readlines(filename),
regex,
[
{:name => ['name']},
{:hier => ['hier','p1']}
]
)
puts h_ary
Output
{:name=>"name1", :hier=>"top_cat/mouse/dog/elephant/horse\r"}
{:name=>"new12", :hier=>"top_ab12/hat[1]/path0_top_ab12/top_ab12path1/cool\r"}
{:name=>"tops", :hier=>"top_bat/car[0]"}
Discussion
Since Ruby 2.0.0 doesn't support branch reset, I have built a solution that add some more power to the s_ary_to_hash function. It now admits a third parameter indicating how to build the final array of hashes.
This third parameter is an array of hashes. Each hash in this array has one key (K) corresponding to the key in the final array of hashes. K is associated with an array containing the named group to use from the passed regex (second parameter of s_ary_to_hash function).
If a group equals nil, s_ary_to_hash skips it for the next group.
If all groups equal nil, K is not pushed on the final array of hashes.
Feel free to modify s_ary_to_hash if this isn't a desired behavior.
Edit: I've changed the method s_ary_to_hash to conform with what I now understand to be the criterion for excluding directories, namely, directory d is to be excluded if there is a downstream directory with the same name, or the same name followed by a non-negative integer in brackets. I've applied that to all directories, though I made have misunderstood the question; perhaps it should apply to the first.
data =<<THE_END
name1 top_cat/mouse/dog/top_cat/mouse/dog/elephant/horse
new12 top_ab12/hat/top_ab12/hat[1]/path0_top_ab12/top_ab12path1/cool
tops top_bat/car[0]
ab123 top_2/top_1/top_3/top_4/top_2/top_1/top_3/top_4/dog
THE_END
text = data.split("\n")
def s_ary_to_hash(ary)
ary.map do |s|
name, _, downstream_path = s.partition(' ').map(&:strip)
arr = []
downstream_dirs = downstream_path.split('/')
downstream_dirs.each {|d| puts "'#{d}'"}
while downstream_dirs.any? do
dir = downstream_dirs.shift
arr << dir unless downstream_dirs.any? { |d|
d == dir || d =~ /#{dir}\[\d+\]/ }
end
{ name: name, hier: arr.join('/') }
end
end
s_ary_to_hash(text)
# => [{:name=>"name1", :hier=>"top_cat/mouse/dog/elephant/horse"},
# {:name=>"new12", :hier=>"top_ab12/hat[1]/path0_top_ab12/top_ab12path1/cool"},
# {:name=>"tops", :hier=>"top_bat/car[0]"},
# {:name=>"ab123", :hier=>"top_2/top_1/top_3/top_4/dog"}]
The exclusion criterion is implement in downstream_dirs.any? { |d| d == dir || d =~ /#{dir}\[\d+\]/ }, where dir is the directory that is being tested and downstream_dirs is an array of all the downstream directories. (When dir is the last directory, downstream_dirs is empty.) Localizing it in this way makes it easy to test and change the exclusion criterion. You could shorten this to a single regex and/or make it a method:
dir exclude_dir?(dir, downstream_dirs)
downstream_dirs.any? { |d| d == dir || d =~ /#{dir}\[\d+\]/ }end
end
Here is a non regexp solution:
result = string.each_line.map do |line|
name, path = line.split(' ')
path = path.split('/')
last_occur_of_root = path.rindex(path.first)
path = path[last_occur_of_root..-1]
{name: name, heir: path.join('/')}
end

How i can sort this hash by created of and key ,value pair

Hi i am little struggle to make this hash and sort it by created of and key , value pair.
Here is my code
hash_answers = {}
unless answers.blank?
answers.each_with_index do |ans ,index|
voted_up_users = ans.votes_up_by_all_users(ans)
voted_down_users = ans.votes_down_by_all_users(ans)
hash_answers[ans.id] = voted_up_users.count -voted_down_users.count #line one
hash_answers[index+1] = ans.created_at # line 2
end
end
if i have line 1 only in code not line 2 then this below code work fine for me
#answers = hash_answers.sort_by { |key, value| value }.reverse
but i also want to sort it by craeted_at
How i can achive this or make hash in another way
Any help will be most appreciated
Thanks
answers.sort_by do |ans|
[ans.net_votes, ans.created_at]
end
Then in your Answers class
def net_votes
votes_up_by_all_users - votes_down_by_all_users
end
You shouldn't have to pass an object to itself as a variable as in ans.votes_up_by_all_users(ans). Objects always know about themselves.
Usually you can sort on a number of things by creating an array of these things and use that as your sort key:
#answers = hash_answers.sort_by { |k, v| [ v[:created_at], v[:count] }
This is dependent on having a sortable structure to start with. You're jamming two entirely different things into the same hash. A better approach might be:
hash_answers[ans.id] = {
:id => ans.id,
:count => voted_up_users.count -voted_down_users.count,
:created_at => ans.created_at
}
You can adjust the order of the elements in the array to sort in the correct order.

Is this the best way to grab common elements from a Hash of arrays?

I'm trying to get a common element from a group of arrays in Ruby. Normally, you can use the
& operator to compare two arrays, which returns elements that are present or common in both arrays. This is all good, except when you're trying to get common elements from more than two arrays. However, I want to get common elements from an unknown, dynamic number of arrays, which are stored in a hash.
I had to resort to using the eval() method in ruby, which executes a string as actual code. Here's the function I wrote:
def get_common_elements_for_hash_of_arrays(hash) # get an array of common elements contained in a hash of arrays, for every array in the hash.
# ["1","2","3"] & ["2","4","5"] & ["2","5","6"] # => ["2"]
# eval("[\"1\",\"2\",\"3\"] & [\"2\",\"4\",\"5\"] & [\"2\",\"5\",\"6\"]") # => ["2"]
eval_string_array = Array.new # an array to store strings of Arrays, ie: "[\"2\",\"5\",\"6\"]", which we will join with & to get all common elements
hash.each do |key, array|
eval_string_array << array.inspect
end
eval_string = eval_string_array.join(" & ") # create eval string delimited with a & so we can get common values
return eval(eval_string)
end
example_hash = {:item_0 => ["1","2","3"], :item_1 => ["2","4","5"], :item_2 => ["2","5","6"] }
puts get_common_elements_for_hash_of_arrays(example_hash) # => 2
This works and is great, but I'm wondering...eval, really? Is this the best way to do it? Are there even any other ways to accomplish this(besides a recursive function, of course). If anyone has any suggestions, I'm all ears.
Otherwise, Feel free to use this code if you need to grab a common item or element from a group or hash of arrays, this code can also easily be adapted to search an array of arrays.
Behold the power of inject! ;)
[[1,2,3],[1,3,5],[1,5,6]].inject(&:&)
=> [1]
As Jordan mentioned, if your version of Ruby lacks support for &-notation, just use
inject{|acc,elem| acc & elem}
Can't you just do a comparison of the first two, take the result and compare it to the next one etc? That seems to meet your criteria.
Why not do this:
def get_common_elements_for_hash_of_arrays(hash)
ret = nil
hash.each do |key, array|
if ret.nil? then
ret = array
else
ret = array & ret
end
end
ret = Array.new if ret.nil? # give back empty array if passed empty hash
return ret
end

Resources