Finding arrays within arrays - ruby

I have an array of hashes, some of which are subsets of others.
a = []
a << {Bob: 1, Mary: 2, Sue: 3}
a << {Bob:1}
a << {Phil: 2, Brian: 8}
a << {Bob: 1, Mary: 2, Sue: 3, Tony: 9}
I need to return an array of the unique super-sets which, in this case, would be:
{Bob: 1, Mary: 2, Sue: 3, Tony: 9}
{Phil: 2, Brian: 8}
I read "Ruby Array Comparison Tricks" however it doesn't offer what I require.
Is there a Ruby solution to compare arrays and identify sub-arrays?

I don't know a great algorithm for this, but the brute force solution is pretty simple in Ruby: use the - operator to find the complement of one array in another, then check if it's empty. With casting so it works with hashes too, the code is something like
def superset?(ary1, ary2)
ary1 != ary2 && (ary2.to_a - ary1.to_a) == []
end
def maximal_sets(arrays)
arrays.reject{ |ary2| arrays.any?{ |ary1| superset?(ary1, ary2) } }
end

Related

idiomatic way to check if array contains ordered (but possibly non-continuous) set of elements

I was wondering if there is a more idiomatic way to get the functionality represented by the code below. Basically I just want to check if the array contains the elements in pattern in the order specified by pattern. It's okay for there to be gaps between these elements.
class Array
def has_pattern?(pattern)
offset = 0
pattern.each do |p|
offset = self[offset..-1].index(p)
return false if offset.nil?
end
return true
end
end
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 4, 5]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([2, 3, 1]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 3, 2]) # false
The code above seems to work, but doesn't feel like idiomatic Ruby to me. Is there a nicer way to write this?
Here's my take on it:
class Array
def has_pattern?(ptn)
i = 0
self.each do |elem|
i += 1 if elem == ptn[i]
end
i >= ptn.size
end
end
It passes through the array only once, so it may make a difference when the array's big.
Here's a different way to approach it:
class Array
def has_pattern?(pattern)
(self - (self - pattern))
.each_cons(pattern.length)
.any? { |p| p === pattern }
end
end
But, as I said in the comments above, I think your solution is superior.

Ruby - pushing values from an array combination to a new array

I am trying to print all the different sums of all combinations in this array [1,2,3]. I want to first push every sum result to a new array b, then print them using b.uniq so that non of the sum results are repeated.
However, with the code I have, the 3 repeats itself, and I think it is because of the way it is pushed into the array b.
Is there a better way of doing this?
a = [1,2,3]
b = []
b.push a
b.push a.combination(2).collect {|a,b| (a+b)}
b.push a.combination(3).collect {|a,b,c| (a+b+c)}
puts b.uniq
p b #[[1, 2, 3], [3, 4, 5], [6]]
Can someone please help me with this? I am still new in ruby.
Because an Array of arbitrary length can be summed using inject(:+), we can create a more general solution by iterating over the range 1..n, where n is the length of the Array.
(1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end.uniq
#=> [1, 2, 3, 4, 5, 6]
By using flat_map, we can avoid getting the nested Array result, and can call uniq directly on it. Another option to ensure uniqueness would be to pass the result to a Set, for which Ruby guarantees uniqueness internally.
require "set"
sums = (1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end
Set.new(sums)
#=> #<Set: {1, 2, 3, 4, 5, 6}>
This will work for an any Array, as long as all elements are Fixnum.
If all you want is an array of the possible sums, flatten the array before getting the unique values.
puts b.flatten.uniq
What is happening is uniq is running over a multi-dimensional array. This causes it to look for duplicate arrays in your array. You'll need the array to be flattened first.

What is the right way to write ruby code?

I am solving the pyramid problem, in which an array is reduced to a single element over time by subtracting two consecutive numbers in each iteration.
input: [1, 5, 9, 2, 3, 5, 6]
iterations
[4, 4, -7, 1, 2, 1],
[0, -11, 8, 1, -1],
[-11, 19, -7, -2],
[30, -26, 5],
[-56, 31],
[87]
output: 87
What is the best way or ruby way to solve this problem? This can be done by inheriting array and making a new class, but I don't know how. Please help. I write this code to solve it:
a = [1,5,9,2,3,5,6]
class Array
def pyr
a = self.each_cons(2).to_a.map! { |e| e[1] - e[0] }
a
end
end
while a.length > 1
a = a.pyr
ans = a[0]
end
p ans
I see three ways to approach this.
Reopen the Array class
Sure, if in your particular ruby script/project this is an elementary functionality of an array, reopen the class. But if you are going to re-open a class, at least make sure the name is something meaningful. pyr? Why not write a full name, so no conflicts are possible, something like next_pyramid_iteration (I have never heard of this pyramid problem, so excuse me if I am way of base here).
Make a class inherit from Array
class Pyramid < Array
def next_iteration
self.each_const(2).map! { |e| e[1] - e[o] }
end
end
and then your calculation would become something like
pyramid = Pyramid.new([1,5,9,2,3,5,6])
while pyramid.length > 1
pyramid.next_iteration
end
pyramid[0]
Make a specific class to do the calculation
I am not quite sure what you are trying to achieve, but why not just make a specific class that knows how to calculate pyramids?
class PyramidCalculator
def initialize(arr)
#pyramid = arr
end
def calculate
while #pyramid.length > 1
do_next_iteration
end
#pyramid.first
end
def self.calculate(arr)
PyramidCalculator.new(arr).calculate
end
protected
def do_next_iteration
#pyramid = #pyramid.each_const(2).map! { |e| e[1] - e[o] }
end
end
because I added a convenience class-method, you can now calculate a result as follows:
PyramidCalculator.calculate([1,5,9,2,3,5,6])
My personal preference would be the last option :)
I would just do it as a two-liner.
a = a.each_cons(2).map{|e1, e2| e2 - e1} while a[1]
a.first # => 87
It's certainly easy enough to turn this into a simple function without hacking on the Array class:
def pyr(ary)
return ary[0] if ary.length < 2
pyr(ary.each_cons(2).map { |e| e[1] - e[0] })
end
p pyr [1,5,9,2,3,5,6] # => 87
Use return ary if you want the answer as a one-element array rather than a scalar.
If you prefer iteration to recursion or have a very large array:
def pyr(ary)
ary = ary.each_cons(2).map { |e| e[1] - e[0] } while ary.length > 1
ary
end
By encapsulating this as a function rather than doing it inline, you get the ability to do the operation on any number of arrays plus it's non-destructive on the original input array.
It's not necessary to compute the end value by successive computation of differences, which requires (n*(n-1)/2 subtractions and the same number of additions, where n is the size of the array a. Instead, we can compute that value by summing n terms of the form:
(-1)K+ibin_coeff(n-1,i)*a[i]
for i = 0..(n-1), where:
K equals 0 if the array has an even number of elements, else K equals 1; and
bin_coeff(n,i) is the binomial coefficient for choosing "n items i at a time" (n!/i!*(n-i)!).
I know what you're thinking: the calculation of each binomial coefficient will take some work. True, but that can be done in an efficient way (which I've not done below), by computing bin_coeff(n-1,i+1) from bin_coeff(n-1,i), etc. Of course, that's academic, as no one is likely to actually use the method I'm suggesting.
(I'm hoping nobody will demand a proof, but I'll try to oblige if a request is made.)
Code
class Fixnum
def factorial
(1..self).reduce(1) { |t,i| t*i }
end
def bin_coeff m
self.factorial/(m.factorial*(self-m).factorial)
end
end
def pyramid_sum(a)
n = a.size-1
sign = n.even? ? -1 : 1
(0..n).reduce(0) do |t,i|
sign = -sign
t + sign * n.bin_coeff(i) * a[i]
end
end
Examples
pyramid_sum [1, 5] #=> 4
pyramid_sum [1, 5, 9] # #=> 0
pyramid_sum [1, 5, 9, 2] #=> -11
pyramid_sum [1, 5, 9, 2, 3] #=> 30
pyramid_sum [1, 5, 9, 2, 3, 5] #=> -56
pyramid_sum [1, 5, 9, 2, 3, 5, 6] #=> 87

Alphabetical sorting of an array without using the sort method

I have been working through Chris Pine's tutorial for Ruby and am currently working on a way to sort an array of names without using sort.
My code is below. It works perfectly but is a step further than I thought I had got!
puts "Please enter some names:"
name = gets.chomp
names = []
while name != ''
names.push name
name = gets.chomp
end
names.each_index do |first|
names.each_index do |second|
if names[first] < names[second]
names[first], names[second] = names[second], names[first]
end
end
end
puts "The names you have entered in alphabetical order are: " + names.join(', ')
It is the sorting that I am having trouble getting my head around.
My understanding of it is that each_index would look at the position of each item in the array. Then the if statement takes each item and if the number is larger than the next it swaps it in the array, continuing to do this until the biggest number is at the start. I would have thought that this would just have reversed my array, however it does sort it alphabetically.
Would someone be able to talk me through how this algorithm is working alphabetically and at what point it is looking at what the starting letters are?
Thanks in advance for your help. I'm sure it is something very straightforward but after much searching I can't quite figure it out!
I think the quick sort algorithm is one of the easier ones to understand:
def qsort arr
return [] if arr.length == 0
pivot = arr.shift
less, more = arr.partition {|e| e < pivot }
qsort(less) + [pivot] + qsort(more)
end
puts qsort(["George","Adam","Michael","Susan","Abigail"])
The idea is that you pick an element (often called the pivot), and then partition the array into elements less than the pivot and those that are greater or equal to the pivot. Then recursively sort each group and combine with the pivot.
I can see why you're puzzled -- I was too. Look at what the algorithm does at each swap. I'm using numbers instead of names to make the order clearer, but it works the same way for strings:
names = [1, 2, 3, 4]
names.each_index do |first|
names.each_index do |second|
if names[first] < names[second]
names[first], names[second] = names[second], names[first]
puts "[#{names.join(', ')}]"
end
end
end
=>
[2, 1, 3, 4]
[3, 1, 2, 4]
[4, 1, 2, 3]
[1, 4, 2, 3]
[1, 2, 4, 3]
[1, 2, 3, 4]
In this case, it started with a sorted list, then made a bunch of swaps, then put things back in order. If you only look at the first couple of swaps, you might be fooled into thinking that it was going to do a descending sort. And the comparison (swap if names[first] < names[second]) certainly seems to imply a descending sort.
The trick is that the relationship between first and second is not ordered; sometimes first is to the left, sometimes it's to the right. Which makes the whole algorithm hard to reason about.
This algorithm is, I guess, a strange implementation of a Bubble Sort, which I normally see implemented like this:
names.each_index do |first|
(first + 1...names.length).each do |second|
if names[first] > names[second]
names[first], names[second] = names[second], names[first]
puts "[#{names.join(', ')}]"
end
end
end
If you run this code on the same array of sorted numbers, it does nothing: the array is already sorted so it swaps nothing. In this version, it takes care to keep second always to the right of first and does a swap only if the value at first is greater than the value at second. So in the first pass (where first is 0), the smallest number winds up in position 0, in the next pass the next smallest number winds up in the next position, etc.
And if you run it on array that reverse sorted, you can see what it's doing:
[3, 4, 2, 1]
[2, 4, 3, 1]
[1, 4, 3, 2]
[1, 3, 4, 2]
[1, 2, 4, 3]
[1, 2, 3, 4]
Finally, here's a way to visualize what's happening in the two algorithms. First the modified version:
0 1 2 3
0 X X X
1 X X
2 X
3
The numbers along the vertical axis represent values for first. The numbers along the horizontal represent values for second. The X indicates a spot at which the algorithm compares and potentially swaps. Note that it's just the portion above the diagonal.
Here's the same visualization for the algorithm that you provided in your question:
0 1 2 3
0 X X X X
1 X X X X
2 X X X X
3 X X X X
This algorithm compares all the possible positions (pointlessly including the values along the diagonal, where first and second are equal). The important bit to notice, though, is that the swaps that happen below and to the left of the diagonal represent cases where second is to the left of first -- the backwards case. And also note that these cases happen after the forward cases.
So essentially, what this algorithm does is reverse sort the array (as you had suspected) and then afterwards forward sort it. Probably not really what was intended, but the code sure is simple.
Your understanding is just a bit off.
You said:
Then the if statement takes each item and if the number is larger than the next it swaps it in the array
But this is not what the if statement is doing.
First, the two blocks enclosing it are simply setting up iterators first and second, which each count from the first to the last element of the array each time through the block. (This is inefficient but we'll leave discussion of efficient sorting for later. Or just see Brian Adkins' answer.)
When you reach the if statement, it is not comparing the indices themselves, but the names which are at those indices.
You can see what's going on by inserting this line just before the if. Though this will make your program quite verbose:
puts "Comparing names[#{first}] which is #{names[first]} to names[#{second}] which is #{names[second]}..."
Alternatively, you can create a new array and use a while loop to append the names in alphabetical order. Delete the elements that have been appended in the loop until there are no elements left in the old array.
sorted_names = []
while names.length!=0
sorted_names << names.min
names.delete(names.min)
end
puts sorted_names
This is the recursive solution for this case
def my_sort(list, new_array = nil)
return new_array if list.size <= 0
if new_array == nil
new_array = []
end
min = list.min
new_array << min
list.delete(min)
my_sort(list, new_array)
end
puts my_sort(["George","Adam","Michael","Susan","Abigail"])
Here is my code to sort items in an array without using the sort or min method, taking into account various forms of each item (e.g. strings, integers, nil):
def sort(objects)
index = 0
sorted_objects = []
while index < objects.length
sorted_item = objects.reduce do |min, item|
min.to_s > item.to_s ? item : min
end
sorted_objects << sorted_item
objects.delete_at(objects.find_index(sorted_item))
end
index += 1
sorted_objects
end
words_2 = %w{all i can say is that my life is pretty plain}
p sort(words_2)
=> ["all", "can", "i", "is", "is", "life", "my", "plain", "pretty", "say", "that"]
mixed_array_1 = ["2", 1, "5", 4, "3"]
p sort(mixed_array_1)
=> [1, "2", "3", 4, "5"]
mixed_array_2 = ["George","Adam","Michael","Susan","Abigail", "", nil, 4, "5", 100]
p sort(mixed_array_2)
=> ["", nil, 100, 4, "5", "Abigail", "Adam", "George", "Michael", "Susan"]

Indexing an array with duplicate elements before and after sorting

This is the basic problem: I have an array of integers with possibly duplicate elements. I need to know the indices of each element, but when I sort the array, whenever I select an element from the new array, I want to be able to reference the same element from the original array.
I am looking for a solution to the problem, or maybe a solution to the approach I am taking.
Here is an array
a = [1, 2, 3, 4, 3, 5, 2]
There are two 2's and two 3's, but if I'm working with the first 2 (from the left), I want to work with index 1, and if I'm working with the second 2, I want to be working with index 6. So I use a helper array to allow me to do this:
helper = [0, 1, 2, 3, 4, 5, 6]
Which I will iterate over and use to access each element from a.
I could have accomplished this with each_with_index, but the problem begins when I sort the array.
Now I have a sort order
sort_order = [2, 4, 1, 5, 3]
I use sort_by to sort a according to sort_order, to produce
sorted_a = [2, 2, 4, 1, 5, 3, 3]
You may assume all elements in the input exist in sort_order to avoid sort_by exceptions.
Now the problem is that my helper array should be updated to match the new positions. Each element should be sorted the same way as a was sorted, because it is unclear whether the first 2 in the new array was at index 1 or at index 6 of the original array.
So my new helper array might look like
new_helper = [1, 6, 3, 0, 5, 2, 4]
So if I were to go with this approach, how would I produce the new_helper array, given the original array and the sort order?
Maybe there is a better way to do this?
I would suggest first zip the original array with the helper array, sort the zipped array according the component coming from the original array, then unzip them (this method does not exist unfortunately, but you can do transpose). Or you can implement your own sorting logic as pointed out by Hunter.
Make a list of pairs of the original data and that data's index. Like this:
a = [(1, 0), (2, 1), (3, 2), (4, 3), (3, 4), (5, 5), (2,6)]
Sort that list (lexicographically, or just ignore the second part of the pair except to carry it along). The second item in every pair tells you where the element was in the original array.
You need to swap the values in the helper array when you swap then in your main array.
loop do
swapped = false
0.upto(list.size-2) do |i|
if list[i] > list[i+1]
list[i], list[i+1] = list[i+1], list[i] # swap values
helper[i], helper[i+1] = helper[i+1], helper[i]; #swap helper values
swapped = true
end
end
break unless swapped
end
Example
irb(main):001:0> def parallel_sort(list, helper)
irb(main):002:1> loop do
irb(main):003:2* swapped = false
irb(main):004:2> 0.upto(list.size-2) do |i|
irb(main):005:3* if list[i] > list[i+1]
irb(main):006:4> list[i], list[i+1] = list[i+1], list[i] # swap values
irb(main):007:4> helper[i], helper[i+1] = helper[i+1], helper[i]; #swap helper values
irb(main):008:4* swapped = true
irb(main):009:4> end
irb(main):010:3> end
irb(main):011:2> break unless swapped
irb(main):012:2> end
irb(main):013:1> return [list, helper]
irb(main):014:1> end
=> nil
irb(main):015:0> a = [3,2,1]
=> [3, 2, 1]
irb(main):016:0> b = ["three","two","one"]
=> ["three", "two", "one"]
irb(main):017:0> parallel_sort(a,b)
=> [[1, 2, 3], ["one", "two", "three"]]
irb(main):018:0>
Sorting inside a loop is rarely a good idea.... If you are doing so, you might be better off with a treap (fast on average but infrequently an operation will take a while) or red-black tree (relatively slow, but gives pretty consistent operation times). These are rather like hash tables, except they're not as fast, and they keep elements stored in order using trees.
Either way, why not use a class that saves both the value to sort by, and the helper value? Then they're always together, and you don't need a custom sorting algorithm.
Since you have sort_order, your array is already kind of sorted, so we should use this fact as an advantage. I came up with this simple solution:
a = [1, 2, 3, 4, 3, 5, 2]
sort_order = [2, 4, 1, 5, 3]
# Save indices
indices = Hash.new { |hash, key| hash[key] = [] }
a.each_with_index { |elem, index| indices[elem] << index }
# Sort the array by placing elements into "right" positions
sorted = []
helper = []
sort_order.each do |elem|
indices[elem].each do |index|
sorted << elem
helper << index
end
end
p sorted
p helper
The algorithm is based on idea of Counting sort, I slightly modified it to save indices.

Resources