What are some nice ways to reverse a nested hash? - ruby

Suppose we have
b = {"b"=>"c"}
By doing b.invertwe can easily obtain the result of
{"c"=>"b"}
Thats when I thought of trying something pretty cool. Suppose we have
a = {"a"=>{"b"=>"c"}}
Whats a fairly efficient way to make this {{"c"=>"b"}=>"a"} (Here we reverse the most inner hash and work our way out)
Of course it would be best to extend this to n amount of hashes within each other. I've been looking for some other questions similar but haven't found any.
Thanks.

This can be accomplished with a recursive method for inverting the keys of the hash (and values, if desired). For example:
hsh = {{"c"=>"b"}=>"a"}
def recursive_invert(hsh)
hsh.each_with_object({}) do |(k, v), inverted_hsh|
if k.is_a? Hash
k = recursive_invert(k)
end
inverted_hsh[v] = k
end
end
recursive_invert(hsh) # {"a"=>{"b"=>"c"}}

Here's A recursive solution that will work in both directions.
def deep_invert(h)
h.each_with_object({}) do |(k,v),obj|
k = deep_invert(k) if k.is_a?(Hash)
v = deep_invert(v) if v.is_a?(Hash)
obj[v] = k
end
end
Example:
a = {"a"=>{"b"=>"c"}}
deep_invert(a)
#=> {{"c"=>"b"}=>"a"}
deep_invert(deep_invert(a)) == a
#=> true

Related

Retrieving max element from SortedSet

I found SortedSet#max, but it seems to be O(N). For now I would do something like this:
s = SortedSet.new((1..100000).to_a.reverse)
where reverse is just to make sure.
min_element = nil; s.each { |x| min_element = x; break }
For maximum element, I would build another SortedSet with all values multiplied by -1 and do the same thing. Is there a more conventional way to do this?
Monkey patch SortedSet.
class SortedSet
def max
#keys[-1]
end
def min
#keys[0]
end
end
That's O(1).

Is this a faithful rendition of the selection sort algorithm?

I've been reading an elementary book about sort algorithms. To get my head around it, I tried to write a simple program to implement the algorithm.
EDIT: I had omitted an important line that was in a previous version - see comment below.
This is my selection sort:
class SelectionSorter
attr_reader :sorted
def initialize(list)
#unsorted = list
#sorted = []
end
def select(list)
smallest = list.first
index = 0
list.each_with_index do |e,i|
if e < smallest
smallest = e
index = i
end
end
#sorted << list.delete_at(index)
end
def sort
#unsorted.length.times { self.select(#unsorted) }
end
end
Here's a test:
require 'minitest/autorun'
require_relative 'sort'
class SelectionSortTest < MiniTest::Test
describe SelectionSorter do
it 'sorts a randomly generated list' do
list = (1..20).map { rand(100-1) + 1 }
sorted_list = list.sort
sorter = SelectionSorter.new(list)
sorter.sort
sorter.sorted.must_equal sorted_list
end
end
end
I'd love comments, particularly around whether this is actually a faithful implementation of the algorithm.
EDIT 2:
OK - here's my in-place code. This is the sort of thing I wanted to avoid, as it feels nastily procedural, with nested loops. However, I think it's a faithful implementation.
class SelectionSorter
def sort(list)
sorted_boundary = (0..(list.length)-1)
sorted_boundary.each do |sorted_index|
smallest_index = sorted_index
smallest_value = list[smallest_index]
comparison = sorted_index + 1
(comparison..(list.length-1)).each do |next_index|
if list[next_index] < smallest_value
smallest_index = next_index
smallest_value = list[smallest_index]
end
end
unless sorted_index == smallest_index
list[smallest_index] = list[sorted_index]
list[sorted_index] = smallest_value
end
end
list
end
end
I'd love to do this in a more recursive fashion, with less stored state, and without nested loops. Any suggestions?
Try adding smallest = e immediately after index = i, so you are keeping a running tally of the smallest value found so far.
I'd also note that selection sort is usually implemented in-place, i.e., scan locations 1-N of your list for the min and then swap it with the first element, then repeat the process with elements 2-N, 3-N, etc. There's no need for a second array or the expense of removing elements from the middle of an array.
I don't know the selection sort algorithm, but I can tell that your code does not do sorting. In this part:
list.each_with_index do |e,i|
if e < smallest
index = i
end
end
you end up having as index the index of the last element of #unsorted that is smaller than the first element of #unsorted (If there is no such element, then index is 0). Then, by:
#sorted << list.delete_at(index)
you take that element from #unsorted, and push it into #sorted. And you repeat this process. That does not give you sort.

How do I detect duplicate values within an array in Ruby?

Say I have an array that looks like:
a = [cat, dog, cat, mouse, rat, dog, cat]
How do I cycle through that, and do something with duplicates - e.g. say delete them?
In other words, if I did a.each do |i|, how do I evaluate a[0], against a[1], a[2], a[3]...and then when I find the one I want, say a[2] in this case has the first duplicate, I then push it to a stack or remove it or something.
I know how to evaluate keys, versus values...but how do I evaluate values against each other within the same array?
Thanks.
You can create a hash to store number of times any element is repeated. Thus iterating over array just once.
h = Hash.new(0)
['a','b','b','c'].each{ |e| h[e] += 1 }
Should result
{"a"=>1, "b"=>2, "c"=>1}
This works efficiently and is rather simple:
require 'set'
visited = Set.new
array.each do |element|
if visited.include?(element)
# duplicated item
else
# first appearance
visited << element
end
end
Try this:
class Array
def find_dups
uniq.map {|v| (self - [v]).size < (self.size - 1) ? v : nil}.compact
end
end
a = ['cat', 'dog', 'cat', 'mouse', 'rat', 'dog', 'cat']
print a - a.find_dups # Removes duplicates
find_dups will return elements that have duplicates
Try this:
array.inject({}){|h, e| h[e] = h[e].to_i + 1; h}
Use
a.uniq! to remove duplicates .
also checkout the ruby-doc.org where you can find more info on ruby's class methods .
A simple solution is to run a double loop:
a.each_with_index do |a1, idx1|
a.each_with_index do |a2, idx2|
next if idx1 >= idx2 # Don't compare element to itself
# and don't repeat comparisons already made
# do something with a pair of elements (a1, a2)
end
end
If you just want to eliminate duplicates, there's a method: Array#uniq.
This will print all the duplicates in an array:
array.inject(Hash.new(0)) { |hash,val|
hash[val] += 1;
hash
}.each_pair { |val,count|
puts "#{val} -> #{count}" if count > 1
}
The best way to do it is to compare it with a unique version of itself. If its the same then it has no duplicates, if not then duplicates exist.
unique_array = original_array.uniq
get a unique version of your array
if original_array == unique_array then return true else return false
compare it to your original array.
Simple!
If you just want to get rid of duplicates, the easiest thing to do is take the array and do array&array. Use the & operator.
If you want to know what those repeats are, just compare array to array&array.
If array is sortable, then something like below will return only the duplicates.
array.sort.each_cons(2).select {|p| p[0] == p[1] }.map &:first
Sorts the array, then maps it to consecutive pairs of elements, selects pairs which are same, maps to elements.

Find second largest number from an array in Ruby

I have an array a = [3,6,774,24,56,2,64,56,34]. I need to find the second largest number in a single iteration using Ruby. How do I achieve it?
Simple:
array.sort[-2]
And you're done :)
This works, but am not sure for the "single iteration"
a.max(2)[1]
sort is probably overkill here, especially for really large arrays. Don't quite understand "single iteration", one line you mean?
a = [3,6,774,24,56,2,64,56,34]
b = a.shift(2).sort
c =
a.inject(b) do |(m2, m), e|
case
when e > m
[m, e]
when e > m2
[e, m]
else
[m2, m]
end
end
c.first #=> 64
Just for fun, this method gets the Nth greatest value in a enumerable (you'd use a bisect module to make the insertion into acc more efficient). As pointed out by #Victor, you would only use it when the length of the array is much bigger than n, othersize a simple array.sort[-n] is faster.
module Enumerable
def max_nth(n)
inject([]) do |acc, x|
(acc + [x]).sort[[acc.size-(n-1), 0].max..-1]
end.first
end
end
p [1, 5, 2, 32, 2, 41, 15, 55].max_nth(2) #=> 41
It works too:
arr.sort.reverse[1]
For me,
a = [3,6,774,24,56,2,64,56,34]
a.uniq # create a new array with the only unique value
a.sort[-2] # find the second greatest value
For me this worked:
data_array5 = [45,65632,232,34345,343,23,23,56]
puts data_array5.sort[data_array5.length()-2]
This is generic you replace 2 with any n number to get nth maxmium
array = [1,2,3,4,5,6]
puts array.max(2).last

More ruby-like solution to this problem?

I am learning ruby and practicing it by solving problems from Project Euler.
This is my solution for problem 12.
# Project Euler problem: 12
# What is the value of the first triangle number to have over five hundred divisors?
require 'prime'
triangle_number = ->(num){ (num *(num + 1)) / 2 }
factor_count = ->(num) do
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
fac_count = exponents.inject(:*)
end
n = 2
loop do
tn = triangle_number.(n)
if factor_count.(tn) >= 500
puts tn
break
end
n += 1
end
Any improvements that can be made to this piece of code?
As others have stated, Rubyists will use methods or blocks way more than lambdas.
Ruby's Enumerable is a very powerful mixin, so I feel it pays here to build an enumerable in a similar way as Prime. So:
require 'prime'
class Triangular
class << self
include Enumerable
def each
sum = 0
1.upto(Float::INFINITY) do |i|
yield sum += i
end
end
end
end
This is very versatile. Just checking it works:
Triangular.first(4) # => [1, 3, 7, 10]
Good. Now you can use it to solve your problem:
def factor_count(num)
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
exponents.inject(1, :*)
end
Triangular.find{|t| factor_count(t) >= 500} # => 76576500
Notes:
Float::INFINITY is new to 1.9.2. Either use 1.0/0, require 'backports' or do a loop if using an earlier version.
The each could be improved by first checking that a block is passed; you'll often see things like:
def each
return to_enum __method__ unless block_given?
# ...
Rather than solve the problem in one go, looking at the individual parts of the problem might help you understand ruby a bit better.
The first part is finding out what the triangle number would be. Since this uses sequence of natural numbers, you can represent this using a range in ruby. Here's an example:
(1..10).to_a => [1,2,3,4,5,6,7,8,9,10]
An array in ruby is considered an enumerable, and ruby provides lots of ways to enumerate over data. Using this notion you can iterate over this array using the each method and pass a block that sums the numbers.
sum = 0
(1..10).each do |x|
sum += x
end
sum => 55
This can also be done using another enumerable method known as inject that will pass what is returned from the previous element to the current element. Using this, you can get the sum in one line. In this example I use 1.upto(10), which will functionally work the same as (1..10).
1.upto(10).inject(0) {|sum, x| sum + x} => 55
Stepping through this, the first time this is called, sum = 0, x = 1, so (sum + x) = 1. Then it passes this to the next element and so sum = 1, x = 2, (sum + x) = 3. Next sum = 3, x = 3, (sum + x) = 6. sum = 6, x = 4, (sum + x) = 10. Etc etc.
That's just the first step of this problem. If you want to learn the language in this way, you should approach each part of the problem and learn what is appropriate to learn for that part, rather than tackling the entire problem.
REFACTORED SOLUTION (though not efficient at all)
def factors(n)
(1..n).select{|x| n % x == 0}
end
def triangle(n)
(n * (n + 1)) / 2
end
n = 2
until factors(triangle(n)).size >= 500
puts n
n += 1
end
puts triangle(n)
It looks like you are coming from writing Ocaml, or another functional language. In Ruby, you would want to use more def to define your methods. Ruby is about staying clean. But that might also be a personal preference.
And rather than a loop do you could while (faction_count(traingle_number(n)) < 500) do but for some that might be too much for one line.

Resources