map! isn't changing the original array - ruby

#key contains an array of four different three-digit integers. someArray has about ten three-digit ints. I have a method that has an array I'm attempting to modify. I'm using map! to accomplish this:
def multiply()
count = 0
#someArray.map! do |map|
if #key[count]
map = map * #key[count]
count = count + 1
else
count = 0
map = map * #key[count]
count = 1
end
end
print #someArray
end
I'm getting a few unexpected results.This prints [1,2,3,4,1,2,3,4,1,2,3,4,1,2]. Why wouldn't this print the map * #key value instead of the count?

.map uses the return value from the block. Your return value is either count = 1 or count = count + 1.
You cannot assign over top of the block's input variable, that has absolutely no effect.
Correctly written, your block would look something like this:
#someArray.map! do |i|
if #key[count]
i *= #key[count]
count = count + 1
else
i *= #key[0]
count = 1
end
i
end
As an aside, this is a slightly inappropriate use of map. There are far better ways of combining the elements of two arrays, even if one of those arrays is shorter.
Given two inputs:
someArray = [1,2,3,4,5,6,7,8,9,0]
key = [2,4,6]
You can combine these two arrays into one array of pairs, using .cycle to produce an enumerator that will wrap around so that both arrays are functionally the same length:
pairs = someArray.zip(key.cycle)
# => [[1, 2], [2, 4], [3, 6], [4, 2], [5, 4], [6, 6], [7, 2], [8, 4], [9, 6], [0, 2]]
Then, you can map the resulting array, multiplying the pairs:
pairs.map { |a,b| a * b }
# => [2, 8, 18, 8, 20, 36, 14, 32, 54, 0]
So, in all, your method would be:
def multiply()
#someArray.zip(#key.cycle).map { |a,b| a * b }
end

Related

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

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]]

Assigning values to a 2D array using "each" method

I'm trying to transpose [[0, 1, 2], [3, 4, 5], [6, 7, 8]]. I get [[2, 5, 8], [2, 5, 8], [2, 5, 8]].
I can see what is happening with the line p transposed_arr but do not understand why this is happening. At every iteration it changes every row instead of only one.
def my_transpose(arr)
# number of rows
m = arr.count
#number of columns
n = arr[0].count
transposed_arr = Array.new(n, Array.new(m))
# loop through the rows
arr.each_with_index do |row, index1|
# loop through the colons of one row
row.each_with_index do |num, index2|
# swap indexes to transpose the initial array
transposed_arr[index2][index1] = num
p transposed_arr
end
end
transposed_arr
end
You need to make only one wee change and your method will work fine. Replace:
transposed_arr = Array.new(n, Array.new(m))
with:
transposed_arr = Array.new(n) { Array.new(m) }
The former makes transposed_arr[i] the same object (an array of size m) for all i. The latter creates a separate array of size m for each i
Case 1:
transposed_arr = Array.new(2, Array.new(2))
transposed_arr[0].object_id
#=> 70235487747860
transposed_arr[1].object_id
#=> 70235487747860
Case 2:
transposed_arr = Array.new(2) { Array.new(2) }
transposed_arr[0].object_id
#=> 70235478805680
transposed_arr[1].object_id
#=> 70235478805660
With that change your method returns:
[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]

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 pick a fractional sample from an array?

I know that ruby has myarray.sample(i) to sample i elements from an array. My problem is that the number of elements are not integers.
i.e I would like a method mysample such that if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
With sample, I will get either 10 or 20 depending on the int conversion. Similarly, if I call myarray.mysample(.25) I want it to return an element with 0.25 probability (that is, it should return one element one times out of four, and three times out of four, it should return an empty array/nil).
How do I do this?
My attempt so far:
def mysample(array,s)
ints = array.sample(s.floor)
if (Random.rand > s - s.floor)
ints << array.sample
end
return ints
end
Is there a better way?
Basing my answer off of this:
if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
Extending Array yields the following:
class Array
def mysample(num)
self.sample( ( num + rand() ).floor )
end
end
> [1, 2, 3, 4, 5].mysample(2.5)
=> [1, 3]
> [1, 2, 3, 4, 5].mysample(2.5)
=> [4, 2, 5]
> [1, 2, 3, 4, 5].mysample(0.5)
=> []
> [1, 2, 3, 4, 5].mysample(0.5)
=> [3]
etc.
To optimal argument is there to decide the spread of randomness for numbers above 1.
class Array
def my_sample(number, deviation=0.3)
if number < 1
return sample rand(100) < number * 100 ? 1 : 0
end
speard = (number*deviation).to_i
randomness = rand(-speard..speard)
sample(number+randomness)
end
end
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> []
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> [3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [9, 2, 1, 4, 10, 7, 3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [7, 2, 3, 8]

Whats the best way to split an array in ruby into multiple smaller arrays of random size

I have multiple arrays in ruby of variable length from 1 to 40 :
#items is a typical array which could be anywhere from 1 to 40 in length. eg
#items = [1, 2, 3, 4, 5, 6]
I want to randomly split the array into smaller arrays of lengths either 1, 2 or 3 to give a result of (for example)
#items = [[1, 2],[3],[4,5,6]]
or
#items = [[1],[2, 3],[4],[5,6]]
etc
I know you can split the array using #items.each_slice(3)... where 3 is a fixed length. But i want to randomly split large arrays of variable length into array sizes of 1,2 or 3 randomly... Whats the best way to achieve this?
items, #items = #items.dup, []
#items.push(items.shift(rand(1..3))) until items.empty?
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = []
until a.empty?
b << a.shift((1..a.size).to_a.sample)
end
# b => [[1, 2], [3, 4, 5, 6, 7], [8, 9], [10]]
# change everytime though
You can limit the sub arrays size by replacing the a.size with 3 or anything you want.
This solution maybe uses too many local variables, but it is non-destructive to the input array and flexible on array window maximum.
def rotateAndTake inputArray, windowSize
rotator, returnArray, breaker = 0, [], true
while breaker do
window = rand(windowSize)+1
if(rotator + window > inputArray.length) then
window = inputArray.length - rotator
breaker = false
end
returnArray << inputArray.rotate(rotator).take(window) if window > 0
rotator += window
end
returnArray
end
Also, I just wanted to write a solution that used the "rotate" method.
Just for yucks, I thought I'd try a pure functional form with no mutating methods for this problem:
( (0..#items.size)
.inject([0]) { |m,_| m + [m.last + 1 + rand(3)] }
.take_while { |i| i < #items.size } + [#items.size] ).
each_cons(2).
map { |s,e| #items[s...e] }
Here's another functional solution:
( [0]+
(1..a.length-1)
.to_a
.sample(rand(a.length))
.sort+
[a.length]
).each_cons(2).map{|i,j| a[i..j-1]}

Resources