Generate a filtered subset of repeated permutations of an array of objects (with given length k) - ruby

I'm new to Ruby. I need to generate all combinations of objects based on a length.
For example, array = [obj1, obj2, obj3], length = 2, then combinations are:
[
[obj1, obj1],
[obj1, obj2],
[obj1, obj3],
# ...
[obj3, obj3]
]
I know I can use repeated_permutation method for this problem, but I need also to be able to filter some permutations. For example, to filter out permutations where 2 identical objects are one after another, i.e. like this [obj1, obj1].

If all you need is to remove any pairs that are the same obj, you can simply use the permutation method.
arr = [1,2,3]
arr.permutation(2).to_a
#=> [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]

Given an arbitrary input array:
a = [1, 2, 3, 3, 4]
If you only wish to generate the unique permutations, then you can simply do:
a.uniq.permutation(2)
(uniq is not needed, if you know the initial array contains unique elements!)
However, as a more general solution, you must do:
a.repeated_permutation(2).reject { |permutation| ** FILTER RULE GOES HERE ** }
So for example, if you wish to filter all results which do not have two consecutive repeated values, then you can do:
a.repeated_permutation(2).reject do |permutation|
permutation.each_cons(2).any? {|x, y| x == y}
end
Taking this to the extreme, here is a generalised method:
def filtered_permutations(array, length)
array.repeated_permutation(length).reject{|permutation| yield(permutation)}
end
# Or, if you prefer:
def filtered_permutations(array, length, &block)
array.repeated_permutation(length).reject(&block)
end
# Usage:
a = [1, 2, 3, 3, 4]
filtered_permutations(a, 2) {|permutation| permutation.each_cons(2).any? {|x, y| x == y} }
# Or, if you prefer:
filtered_permutations(a, 2) {|permutation| permutation.each_cons(2).any? {|consecutive| consecutive.uniq.one?} }

Pass a block where you perform your "filtering". So to remove those with identical elements you'd go with:
a = [1,2,3]
a.repeated_permutation(2).reject { |permutation| permutation.uniq.one? }
#=> [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]

Related

How to convert arrays of numbers into a matrix in Ruby

I have a two simple arrays of numbers, representing the cartesian position of an object.
a = [3, 4]
b = [8, 5]
I want to check if "a" and "b" are beside each other. I would like to convert the two into a matrix and perform a subtractions of the two positions, and then check if the absolute value of either element is "1".
Is there a way to do this?
You're getting the uninitialized constant error because you first need:
require 'matrix'
Then you could just:
Matrix[a,b]
Sample interactive output:
irb(main):011:0> require 'matrix'
=> true
irb(main):012:0> Matrix[a,b]
=> Matrix[[3, 4], [8, 5]]
I don't think using Matrix class methods is justified here. The only method that would be marginally useful is Matrix#-, but to use that you need to convert your arrays to Matrix objects, apply Matrix#-, then convert the resultant matrix object back to an array to determine if the absolute value of any element equals one (whew!). I'd just do this:
def adjacent?(a,b)
a.zip(b).any? { |i,j| (i-j).abs == 1 }
end
adjacent?([3, 4], [8, 5]) #=> true
adjacent?([3, 7], [8, 5]) #=> false
adjacent?([3, 7], [2, 5]) #=> true
For the first example:
a = [3, 4]
b = [8, 5]
c = a.zip(b)
#=> [[3, 8], [4, 5]]
c.any? { |i,j| (i-j).abs == 1 }
#=> true
The last statements determines if either of the following is true.
(3-8).abs == 1
(4-5).abs == 1

How do I explode an internal array in Ruby if the internal count is less than a certain value?

Using Ruby 2.1, if I have an array like:
[[1,1], [2,3], [5,8], [6, 4]]
How can I convert that to an array that only has internal arrays with a count > 3?
For example, it should be:
[1, 2, 2, 2, [5,8], [6,4]]
So [5,8] and [6,4] would "pass" because their counts are > 3 but [1,1] and [2,3] would "fail" and explode out because their counts are < than 4.
EDIT
Sorry, I wasn't very clear. By "counts" I mean the second value in the internal arrays. For example, the [2,3] would have a value of 2 and a count of 3. [5,8] would have a value of 5 and a count of 8.
So if the count is > 3 then keep the original array. If the count is 3 or less, then explode the value out count number of times.
I'm pretty sure someone can come up with a better way of doing this, but:
input = [[1,1], [2,3], [5,8], [6, 4]]
input.flat_map {|val, ct| ct > 3 ? [[val, ct]] : Array.new(ct, val) }
# => [1, 2, 2, 2, [5, 8], [6, 4]]
The basic idea is that we just map the inputs (each entry) to an output (the original entry or an exploded list of values) by the count. I'm using flat_map here, but you could use the same technique with map {}.flatten(1) if you wanted. You could also use inject or each_with_object to collect the output values, which may be more straightforward but slightly less terse.
Try this:
data = [[1,1], [2,3], [5,8], [6, 4]]
results = []
data.each do |arr|
val, count = arr
if count > 3
results << arr
else
results.concat [val] * count
end
end
p results
--output:--
[1, 2, 2, 2, [5, 8], [6, 4]]
arr = [[1,1], [2,3], [5,8], [6, 4]]
arr.flat_map { |a| (a.last > 3) ? [a] : [a.first]*a.last }
#=> [1, 2, 2, 2, [5, 8], [6, 4]]
Thanks to #ChrisHeald for pointing out that flat_map is equivalent to map {}.flatten(1) (I previously had the latter) and to #7stud for telling me my original solution was incorrect, which gave me the opportunity to make my solution more interesting as well as (hopefully) correct.

Finding the mode of a Ruby Array (simplified_

I'm trying to find the mode of an Array. Mode = the element(s) that appear with the most frequency.
I know there are lots of tricks with #enumerable, but I'm not there yet in my learning. The exercise I'm doing assumes I can solve this problem without understanding enumerable.
I've written out my game plan, but I'm stuck on the 2nd part. I'm not sure if it's possible to compare a hash key against an array, and if found, increment the value.
def mode(array)
# Push array elements to hash. Hash should overwrite dup keys.
myhash = {}
array.each do |x|
myhash[x] = 0
end
# compare Hash keys to Array. When found, push +=1 to hash's value.
if myhash[k] == array[x]
myhash[k] += 1
end
# Sort hash by value
# Grab the highest hash value
# Return key(s) per the highest hash value
# rejoice!
end
test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
mode(test) # => 3, 6 (because they each appear 3 times)
You can create a hash with a default initial value:
myhash = Hash.new(0)
Then increment specific occurrences:
myhash["foo"] += 1
myhash["bar"] += 7
myhash["bar"] += 3
p myhash # {"foo"=>1, "bar"=>10}
With that understanding, if you replace your initial hash declaration and then do the incrementing in your array.each iterator, you're practically done.
myhash.sort_by{|key,value| value}[-1]
gives the last entry in the sorted set of hash values, which should be your mode. Note that there may be multiple modes, so you can iterate backwards while the value portion remains constant to determine them all.
There are many, many ways you could do this. Here are a few.
#1
array = [3,1,4,5,4,3]
a = array.uniq #=> [3, 1, 4, 5]
.map {|e| [e, array.count(e)]}
#=> [[3, 2], [1, 1], [4, 2], [5, 1]]
.sort_by {|_,cnt| -cnt} #=> [[3, 2], [4, 2], [1, 1], [5, 1]]
a.take_while {|_,cnt| cnt == a.first.last}
#=> [[3, 2], [4, 2]]
.map(&:first) #=> [3, 4]
#2
array.sort #=> [1, 3, 3, 4, 4, 5]
.chunk {|e| e}
#<Enumerator: #<Enumerator::Generator:0x000001021820b0>:each>
.map { |e,a| [e, a.size] } #=> [[1, 1], [3, 2], [4, 2], [5, 1]]
.sort_by { |_,cnt| -cnt } #=> [[4, 2], [3, 2], [1, 1], [5, 1]]
.chunk(&:last)
#<Enumerator: #<Enumerator::Generator:0x00000103037e70>:each>
.first #=> [2, [[4, 2], [3, 2]]]
.last #=> [[4, 2], [3, 2]]
.map(&:first) #=> [4, 3]
#3
h = array.each_with_object({}) { |e,h|
(h[e] || 0) += 1 } #=> {3=>2, 1=>1, 4=>2, 5=>1}
max_cnt = h.values.max #=> 2
h.select { |_,cnt| cnt == max_cnt }.keys
#=> [3, 4]
#4
a = array.group_by { |e| e } #=> {3=>[3, 3], 1=>[1], 4=>[4, 4], 5=>[5]}
.map {|e,ees| [e,ees.size]}
#=> [[3, 2], [1, 1], [4, 2], [5, 1]]
max = a.max_by(&:last) #=> [3, 2]
.last #=> 2
a.select {|_,cnt| cnt == max}.map(&:first)
#=> [3, 4]
In your approach, you have first initialized a hash containing keys taken from the unique values of the array, with the associated values all set to zero. For example, the array [1,2,2,3] would create the hash {1: 0, 2: 0, 3: 0}.
After this, you plan to count the instances of each of the values in the array by incrementing the value for the associated key in the hash by one for each instance. So, after finding the number 1 in the array, the hash would look like so: {1: 1, 2: 0, 3: 0}. You clearly need to do this for each value in the array, so given your approach and current level of understanding, I would suggest looping through the array again:
array.each do |x|
myhash[x] += 1
end
As you can see, we don't need to check that myhash[k] == array[x] since we have already created a key:value pair for each number in the array.
However, while this approach will work, it's not very efficient: we're having to loop through the array twice. The first time to initialize all the key:value pairs to some default (zero, in this case), and the second to count the frequencies of each number.
Since the default value for each key will be zero, we can remove the need to initialize the defaults by using a different hash constructor. myhash = {} will return nil if we access a key that doesn't exist, but myhash = Hash.new(0) will return 0 if we access a non-existent key (note that you could provide any other value or variable, if required).
By providing a default value of zero, we can get rid of the first loop entirely. When the second loop finds a key that doesn't exist, it will use the default provided and automatically initialize it.
def mode(array)
array.group_by{ |e| e }.group_by{ |k, v| v.size }.max.pop.map{ |e| e.shift }
end
Using the simple_stats gem:
test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
test.modes #=> [3, 6]
If it is an unsorted array, we can sort the array in descending order
array = array.sort!
Then use the sorted array to create a hash default 0 and with each element of the array as a key and number of occurrence as the value
hash = Hash.new(0)
array.each {|i| hash[i] +=1 }
Then mode will be the first element if the hash is sorted in descending order of value(number of occurrences)
mode = hash.sort_by{|key, value| -value}.first[0]

How do I add a cumulative sum to an array for only one value?

I have an array of arrays with x and y values:
[[some_date1, 1], [some_date2, 3], [some_date3, 5], [some_date4, 7]]
The result should only sum the y values (1, 3, 5, 7) so that the result is like this:
[[some_date1, 1], [some_date2, 4], [some_date3, 9], [some_date4, 16]]
How is this possible in Ruby?
Yes, this is possible in Ruby. You can use [map][1] and do something like this:
sum = 0
array.map {|x,y| [x, (sum+=y)]}
This is how it works. For the given the input:
array = ["one", 1], ["two", 2]
It will iterate through each of the elements in the array e.g.) the first element would be ["one", 1].
It will then take that element (which is an array itself) and assign the variable x to the first element in that array e.g.) "one" and y to the second e.g.) 1.
Finally, it will return an array with the result like this:
=> ["one", 1], ["two", 3]
You can use map:
a = [[:some_date1, 1], [:some_date2, 3], [:some_date3, 5], [:some_date4, 7]]
sum = 0
a.map { |f, v| [f, (sum = sum + v)]}
=> [[:some_date1, 1], [:some_date2, 4], [:some_date3, 9], [:some_date4, 16]]
Since sum will be nil in the first iteration it is necessary to call to_i on it.
a = [['some_date1', 1], ['some_date2', 3], ['some_date3', 5], ['some_date4', 7]]
a.each_cons(2){|a1, a2| a2[1] += a1[1]}
last = 0
arr.map do |a, b|
last = last + b
[a, last]
end
I'd use:
ary = [['some_date1', 1], ['some_date2', 3], ['some_date3', 5], ['some_date4', 7]]
ary.inject(0) { |m, a|
m += a[-1]
a[-1] = m
}
After running, ary is:
[["some_date1", 1], ["some_date2", 4], ["some_date3", 9], ["some_date4", 16]]
The reason I prefer this is it doesn't require the addition of an accumulator variable. inject returns a value but it gets thrown away without an assignment.

Number of arguments in a ruby block

I'm new to Ruby, but not to languages that allow lambda's, such as groovy. So I saw this example:
myArray.product(otherArray).reject{|i,j| i > j}
in a ruby code block, and I hadn't seen this block take 2 arguments before, but when I went to look at the documentation I can only see the documentation that says that it takes 1 argument. I looked at the same for the enumerable class, but that doc only shows 1 argument also.
I understand that it works, I guess I was hoping that there was an easier way to determine how many arguments it takes other then a guess and test method. How can I tell how many arguments a block takes in Ruby?
This works because Ruby supports destructuring.
Destructuring allows you to bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable.
This allows the following to hold true:
arr = [1, 2]
x = arr
x == [1, 2] # true
y, z = arr
y == 1 # true
z == 2 # true
You can see from the following code that destructuring in arguments to blocks isn't unique to the built-in methods that take a block:
def my_method(arr)
yield arr
end
my_method([1, 2, 3]) {|x| puts x.inspect }
# => [1, 2, 3]
my_method([1, 2, 3]) {|x, y, z| puts x.inspect }
# => 1
Check out Destructuring with Ruby for more information.
You can do some interesting restructuring in block parameters, depending on the structure of your array:
[[1, 2], [3, 4], [5, 6], [7, 8]].reject {|x,y| y == 8 }
#=> [[1, 2], [3, 4], [5, 6]]
You can group them in parentheses:
[ [[1,2],3], [[1,3],6] ].select {|(x,y),z| x == 1 && z == 3 }
#=> [ [[1,2],3] ]
You can also use the splat operator for various things, like dealing with variable-length subarrays:
[[:a,:b,2,3,4,5,6], [:c,:d,7,8,9]].each {|x,y,*numbers| puts numbers.inspect }
#=> [2,3,4,5,6]
#=> [7,8,9]
Ruby is flexible in how it interprets the arguments; here is a similar example, with one and then two arguments:
[1, 3].product([2, 4]).reject {|a| a.first > a.last }
=> [[1, 2], [1, 4], [3, 4]]
[1, 3].product([2, 4]).reject {|a,b| a > b }
=> [[1, 2], [1, 4], [3, 4]]
The rule of thumb here is that you can treat the arguments either as a composite object, or as individual elements in a collection. E.g.,
[1, 2, 3].tap {|a,b,c| puts [a,b,c].inspect }
[1, 2, 3]
...
[1, 2, 3].tap {|a,b| puts [a,b].inspect }
[1, 2]
...
[1, 2, 3].tap {|a| puts a.inspect }
[1, 2, 3]

Resources