Finding all duplicates of a Ruby Array - ruby

I am looking for an efficient way to give all the duplicate values for an array.
Example:
a = [1,2,2,3,4,7,4,4]
dups(a)
#[[2,2],[4,4,4]]
I've tried using brute force it by doing:
def dups(array)
dups = []
array.uniq.each do |i|
found = array.select{|s| s.eql?(i)}
dups = dups.push found if found.size > 1
end
return dups
end
But it is pretty slow if you small percent of duplicate, since you are looping through the entire array.

def dup_group_by(array)
array.group_by(&:itself).filter_map { |_,v| v if v.size > 1 }
end
The key was group_by method which was in the Enumerable module, I was only looking in the Array class.

Related

Ruby iterate through hash and compare value pairs

My Ruby assignment is to iterate through a hash and return the key associated with the lowest value, without using any of the following methods:
#keys #values #min #sort #min_by
I don't understand how to iterate through the hash and store each pair as it comes through, compare it to the last pair that came through, and return the lowest key. This is my code to show you my thought process, but it of course does not work. Any thoughts on how to do this? Thanks!
def key_for_min_value(name_hash)
index = 0
lowest_hash = {}
name_hash.collect do |key, value|
if value[index] < value[index + 1]
lowest = value
index = index + 1
key_for_min_value[value]
return lowest
end
end
end
Track min_value and key_for_min_value. Iterate through the hash, and any time the current value is lower than min_value, update both of these vars. At the end of the loop, return key_for_min_value.
I didn't include sample code because, hey, this is homework. :) Good luck!
One way to do it is transforming our hash into an array;
def key_for_min_value(name_hash)
# Convert hash to array
name_a = name_hash.to_a
# Default key value
d_value= 1000
d_key= 0
# Iterate new array
name_a.each do |i|
# If current value is lower than default, change value&key
if i[1] < d_value
d_value = i[1]
d_key = i[0]
end
end
return d_key
end
You might need to change d_value to something higher or find something more creative :)
We can use Enumerable#reduce method to compare entries and pick the smallest value. Each hash entry gets passed in as an array with 2 elements in reduce method, hence, I am using Array#first and Array#last methods to access key and values.
h = {"a" => 1, "b" => 2, "c" => 0}
p h.reduce{ |f, s| f.last > s.last ? s : f }.first
#=> "c"

How to group anagrams together of a array in Ruby

I am trying to write a function called anagram() and return the following output:
Example = ['Creams', 'bart','bAtr', 'bar', 'rabt', 'Cars','creamsery', 'Scar', 'scream']
puts anagram(Example)
OUTPUT:
[["Creams", "scream"] ["bart", "bAtr", "rabt"], ["bar"], ["Cars","Scar"],["creamsery"]]
I know I can use:
Example[i].downcase.char.sort.join
to compare each element in the array, but I have a hard time grouping them all together inside a loop.
What about this:
def anagrams(ary)
h = Hash.new([])
ary.each.with_index { |el, i| h[el.downcase.chars.sort.join] += [i] }
h.map { |key, indexes| indexes.map { |i| ary[i] } }
end
The function saves the indexes in a hash and then returns the corresponding elements.
This approach scans the array at most twice, therefore it's O(n). Even if it is not particularly elegant, it is quite fast.
What about this:
def anagram(ary)
ary.map do |el|
ary.select{ |x| el.downcase.chars.sort.join('') == x.downcase.chars.sort.join('') }
end.uniq
end
Calling .chars on a String will provide you an Array of chars that you can sort and then .join back to a String.
You can try it on the console:
> ary = ['Creams', 'bart','bAtr', 'bar', 'rabt', 'Cars','creamsery', 'Scar', 'scream']
> anagram ary
=> [["Creams", "scream"], ["bart", "bAtr", "rabt"], ["bar"], ["Cars", "Scar"], ["creamsery"]]
Of course, this is not particularly elegant or efficient. I'm eager to learn from others.
Example.group_by{|w| w.downcase.chars.sort}.values
One liner -
array.inject({}){|hash, ele| key = ele.chars.sort.join.downcase; hash[key] = (hash[key] || []) + [ele]; hash }.values
Complexity - O(n * mlogm), where n => number of elements, m => max size of string length.
If value of m is negligible comparing to n then complexity will be O(n)

Values in the while loop do not modify outside values

I have a long code but I tried to copy and adapt my problem in as few lines as possible . I have a method which creates an array( 2D ) with 0 and 1
array1 = newValue(2) - the number 2 represents how many 1 the array has
array2 = newValue(3)
and this loop
(0..9).each do|i|
(0..9).each do|j|
while((array1[i][j] == array2[i][j]) && (array2[i][j] == 1)) do
array1 = newvalue(2)
array2 = newvalue(3)
end
end
end
I'm using the while loop so I won t have a 1 in the same position in both arrays . But what is inside the while loop doesn't modify the values of the array . I also tried using map!/collect! but I think I did something wrong because nothing happened. I hope you can understand what I was trying to do .
Edit:
def newValue(value)
value = value.to_i
array = Array.new(10) { Array.new(10 , 0) }
(a lot of conditions on how to position the items in the array)
return array
end
Here's my take... hopefully it'll help out. It seems that what you noticed was true. The arrays are not getting reset. Probably because inside the each blocks, the scope is lost. This is probably because the are arrays. I took a slightly different approach. Put everything in a class so you can have instance variables that you can control and you know where they are and that they are always the same.
I pulled out the compare_arrays function which just returns the coordinates of the match if there is one. If not it returns nil. Then, youre while loop is simplified in the reprocess method. If you found a match, reprocess until you don't have a match any more. I used a dummy newValue method that just returned another 2d array (as you suggested yours does). This seems to do the trick from what I can tell. Give it a whirl and see what you think. You can access the two arrays after all the processing with processor.array1 as you can see I did at the bottom.
# generate a random 2d array with 0's and val's
def generateRandomArray(val=1)
array = []
(0..9).each do |i|
(0..9).each do |j|
array[i] ||= []
array[i][j] = (rand > 0.1) ? 0 : val
end
end
array
end
array1 = generateRandomArray
array2 = generateRandomArray
def newValue(val)
generateRandomArray(val)
end
class Processor
attr_reader :array1, :array2
def initialize(array1, array2)
#array1 = array1
#array2 = array2
end
def compare_arrays
found = false
for ii in 0..9
break unless for jj in 0..9
if ((#array2[ii][jj] == 1) && (#array1[ii][jj] == 1))
found = true
break
end
end
end
[ii,jj] if found
end
def reprocess
while compare_arrays
puts "Reprocessing"
#array1 = newValue(2)
#array2 = newValue(3)
reprocess
end
end
end
processor = Processor.new(array1, array2)
processor.reprocess
puts processor.array1.inspect

Ruby array of hashes values to string

I have an array of hashes (#1) that looks like this:
data = [{"username"=>"Luck", "mail"=>"root#localhost.net", "active"=>0}]
that I am trying to compare with following array of hashes (#2):
test = [{"username"=>"Luck", "mail"=>"root#localhost.net", "active"=>"0"}]
where #1 I obtained from database by mysql2 (what actually is in the database)
and #2 from my cucumber scenario (what I minimally expect ot be there).
By definition #2 must be a subset of #1 so I follow with this code:
data = data.to_set
test = test.to_set
assert test.subset?(data)
The problem is in data array the value of active is NOT a string. In case of data it is Fixnum, and in case of test, it is String.
I need a solution that will work even for more than one hash in the array. (As the database can return more than one row of results) That is why I convert to sets and use subset?
From other questions I got:
data.each do |obj|
obj.map do |k, v|
{k => v.to_s}
end
end
However it does not work for me. Any ideas?
Assumptions you can make:
All the keys in data will always be Strings.
All the keys in test will always be Strings. And always be the identical to data.
All the values in test will always be Strings.
Here are a couple of approaches that should do it, assuming I understand the question correctly.
#1: convert the hash values to strings
def stringify_hash_values(h)
h.each_with_object({}) { |(k,v),h| h[k] = v.to_s }
end
def sorta_subset?(data,test)
(test.map { |h| stringify_hash_values(data) } -
data.map { |h| stringify_hash_values(data) }).empty?
end
data = [{"username"=>"Luck", "mail"=>"root#localhost.net", "active"=>0}]
test = [{"username"=>"Luck", "mail"=>"root#localhost.net", "active"=>"0"}]
sorta_subset?(data,test) #=> true
#2 see if keys are the same and values converted to strings are equal
require 'set'
def hashes_sorta_equal?(h,g)
hk = h.keys
(hk.to_set == g.keys.to_set) &&
(h.values_at(*hk).map(&:to_s) == g.values_at(*hk).map(&:to_s))
end
def sorta_subset?(data,test)
test.all? { |h| data.any? { |g| hashes_sorta_equal?(g,h) } }
end
sorta_subset?(data,test) #=> true
Don't ask me why it works, but I found A solution:
data.map! do |obj|
obj.each do |k, v|
obj[k] = "#{v}"
end
end
I think it has something to do with what functions on arrays and hashes change the object itself and not create a changed copy of the object.

difficulty modifying two dimensional ruby array

Excuse the newbie question. I'm trying to create a two dimensional array in ruby, and initialise all its values to 1. My code is creating the two dimensional array just fine, but fails to modify any of its values.
Can anyone explain what I'm doing wrong?
def mda(width,height)
#make a two dimensional array
a = Array.new(width)
a.map! { Array.new(height) }
#init all its values to 1
a.each do |row|
row.each do |column|
column = 1
end
end
return a
end
It the line row.each do |column| the variable column is the copy of the value in row. You can't edit its value in such way. You must do:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.each do |row|
row.map!{1}
end
return a
end
Or better:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
a = Array.new(width){ Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
Array.new(width) { Array.new(height){1} }
end
each passes into the block parameter the value of each element, not the element itself, so column = 1 doesn't actually modify the array.
You can do this in one step, though - see the API docs for details on the various forms of Array#new. Try a = Array.new(width) {|i| Array.new(height) {|j| 1 } }
you can create it like this?
a=Array.new(width) { Array.new(height,1) }
column in your nested each loop is a copy of the value at that place in the array, not a pointer/reference to it, so when you change its value you're only changing the value of the copy (which ceases to exist outside the block).
If you just want a two-dimensional array populated with 1s something as simple as this will work:
def mda(width,height)
[ [1] * width ] * height
end
Pretty simple.
By the way, if you want to know how to modify the elements of a two-dimensional array as you're iterating over it, here's one way (starting from line 6 in your code):
#init all its values to 1
a.length.times do |i|
a[i].length.times do |j|
a[i][j] = 1
end
end

Resources