How to find the list of pairs in an array using ruby? - ruby

Input:
a = [4, 5, 5, 5, 6, 6, 4, 1, 4, 4, 3, 6, 6, 3, 6, 1, 4, 5, 5, 5]
How to list out no of pairs in an array.
Output:
9
Description
#no 1(1 pair)
#no 3(1 pair)
#no 4(2 pairs)
#no 5(3 pairs)
#no 6(2 pairs)
#so total 9 pairs

Here is another option:
a.group_by(&:itself).transform_values{ |v| v.size / 2 }.values.sum
#=> 9
How it works.
First group the elements by value:
a.group_by(&:itself) #=> {4=>[4, 4, 4, 4, 4], 5=>[5, 5, 5, 5, 5, 5], 6=>[6, 6, 6, 6, 6], 1=>[1, 1], 3=>[3, 3]}
Then transforming the keys to the pair count:
a.group_by(&:itself).transform_values{ |v| v.size / 2 } #=> {4=>2, 5=>3, 6=>2, 1=>1, 3=>1}
So, get the values of the hash:
a.group_by(&:itself).transform_values{ |v| v.size / 2 }.values #=> [2, 3, 2, 1, 1]
Finally, sum the values, which is the first line of code posted above.

arr = [4, 5, 5, 5, 6, 6, 4, 1, 4, 4, 3, 6, 6, 3, 6, 1, 4, 5, 5, 5]
hash = Hash.new(0)
arr.each { |e| hash[e] += 1 }
hash.values.reduce(0) { |s, n| s += n / 2 } // => 9
Since from what I can gather you are basically removing integers the moment they got paired once so technically it's just an integer division by two.
[1] How to count identical string elements in a Ruby array
[2] Reduce Hash Values

I have done like this, It works
b = []
a.uniq.each { |i| b.push(a.count(i)/2)}
b.sum

Related

Ruby array processing add an unexpected nil to the new array?

New to Ruby (coming from Python) and try to experiment this exercise:
(mixed the array items by taking first, last in rotating fashion)
Expected Output to be - [1, 7, 2, 6, 3, 5, 4]. But I did not expect 'nil' at the end... The orig. array can contain even or odd size of numbers.
Can someone shed the light of this unexpected? Thanks in advance.
[Updates - re-write the example from Ruby Cookbook p.162 Array ]
nums = (1..7).to_a # [1, 2, 3, 4, 5, 6, 7]
mixed = []
#middle = nums.length / 2
#index = 0
until nums.empty?
mixed << nums.shift(). #get 1st element out
mixed << nums.pop() #get last element out
#index += 1
end
print mixed # Got [1, 7, 2, 6, 3, 5, 4, nil]
What's happening is that the total num of elements in the array is odd so the last value is put into mixed on 'shift' and then there is no element left in the array. This will solve your issue:
nums = (1..7).to_a # [1, 2, 3, 4, 5, 6, 7]
mixed = []
#middle = nums.length / 2
#index = 0
until nums.empty?
mixed << nums.shift()
mixed << nums.pop() unless nums.empty?
#index += 1
end
print mixed # Got [1, 7, 2, 6, 3, 5, 4]
Another way is: If the num of elements is odd then run the loop till n-1 and then get the last element out using shift/pop (doesn't matter if you use shift or pop at the end, you will get the same element.)
The Cookbook method can be made non-destructive (avoid modifying nums) as follows.
def doit(nums)
nums.size.times.map { |i| i.even? ? nums[i/2] : nums[-i/2] }
end
doit [1, 2, 3, 4, 5, 6, 7]
#=> [1, 7, 2, 6, 3, 5, 4]
doit [1, 2, 3, 5, 6, 7]
#=> [1, 7, 2, 6, 3, 5]
Here is another (non-destructive) way to do that.
def doit(nums)
n = nums.size/2
nums.first(n).zip(nums.last(n).reverse).flatten.tap do |a|
a << nums[n] if nums.size.odd?
end
end
doit [1, 2, 3, 4, 5, 6, 7]
#=> [1, 7, 2, 6, 3, 5, 4]
doit [1, 2, 3, 5, 6, 7]
#=> [1, 7, 2, 6, 3, 5]

How to find highest char value in ruby?

Suppose I'm given a string "Nas". I want to select the char which has the highest integer value. How can I do this in Ruby?
My implementation involved creating a hash with the numerical value for each char:
alpha = {}
('a'...'z').zip(1. .26).each do |x| alpha[x[0]] = x[1] end
And then I'd loop through my word like so:
word.each_char do |c |
puts c
end
I have defined a method which takes two parameters, the first param is an array which specifies the "importance of a char", and second param takes a word:
def designer_pdf(h, word)
alpha = {}
('a'...'z').zip(1..26).each do |x|
alpha[x[0]] = x[1]
end
word.each_char do |c|
puts c
end
end
designer_pdf (
[1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
'abc'
)
The goal is two return the highest value from the h array based on the character from word.
For example, word: 'abc':
a = 1
b = 3
c = 1
So return 3 because b is highest.
You can do this
For zep string
p [*'a'..'z'].zip([1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])
.select{|x|("zeb".chars).include?x[0]}.max_by{|x|x[1]}
output
["z", 5]
You can pass any priority array and any word as arguments:
def designer_pdf(h, word)
alpha = Hash[('a'..'z').zip(h)]
# if you want only character from word with highest priority
character = word.chars.max_by{|e| alpha[e]}
# if you want only highest char value
value = alpha[character]
# if you want both character and value
[character, value]
end
> designer_pdf([1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], "abc")
#=> ["b", 3]
> designer_pdf([1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], "zeb")
#=> ["z", 5]
There is no need to construct a hash.
def largest_mapped_value(word, values)
base = 'a'.ord
values[word.each_char.max_by { |c| values[c.ord-base] }.ord-base]
end
# a b c d e f g h i j k l m n o p q r s t
values = [1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 6, 5, 5, 7, 5, 5, 5, 5, 5, 1,
5, 5, 5, 5, 5, 5]
# u v w x y z
%w| cat kite zebra fined a |.each { |word|
puts "#{word}: #{largest_mapped_value(word, values)}" }
cat: 1
kite: 6
zebra: 5
fined: 7
a: 1
A variant is the following.
word = "fined"
base = 'a'.ord
#=> 97
word.each_char.map { |c| values[c.ord-base] }.max
#=> 7

ruby find the index of the next available number

I have a find_num method that returns the index of a specified number in an ordered array, e.g.
find_num(6, [1, 4, 6, 9, 13]) #=> 2
however my spec also requires that if the number is not available it finds the position of the next highest number so ...
find_num(8, [1, 4, 6, 9, 13]) #=> 3
as 9 is the next available number.
Having trouble implementing this... I have thought of iterating through the whole array but I am told to take into account the array could be large...
You can pass a block to index and it ...
[...] returns the index of the first object for which the block returns true. Returns nil if no match is found.
Examples:
[1, 4, 6, 9, 13].index { |n| n >= 6 } #=> 2
[1, 4, 6, 9, 13].index { |n| n >= 8 } #=> 3
[1, 4, 6, 9, 13].index { |n| n >= 15 } #=> nil
Because this requires the array to be ordered, you can also use bsearch_index which performs a binary search.
you can also find a index of any element in array like this.
2.1.8 :040 > [1, 4, 6, 9, 13].index(6)
=> 2
2.1.8 :041 > [1, 4, 6, 9, 13].index(15)
=> nil
def find_num(n,a)
a.each_with_index.to_a.sort_by(&:first).find { |nbr,_| nbr >= n }.last
end
find_num(6, [1, 4, 6, 9, 13])
#=> 2
find_num(8, [1, 4, 6, 9, 13]) #=> 3
#=> 3
The steps for
n = 8
a = [1, 4, 6, 9, 13]
are as follows.
b = a.each_with_index.to_a
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
c = b.sort_by(&:first)
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
d = c.find { |nbr,_| nbr >= n }
#=> [9, 3]
d.last
#=> 3

comparing 2 arrays in every position

So what Im trying to accomplish is write a (shorter) condition that makes sure each element is different from the other array. This is confusing but I hope this example clears it up.
array = [1, 2, 3]
new_array = array.shuffle
until array[0] != new_array[0] &&
array[1] != new_array[1] &&
array[2] != new_array[2]
new_array = array.shuffle
end
So what Im doing is making sure that every single element/index pair does not match in the other array.
# [1, 2, 3] => [3, 1, 2] yayyyy
# [1, 2, 3] => [3, 2, 1] not what I want because the 2 didnt move
Is there a better way to do what I want to do? Ive looked up the .any? and .none? but I cant seem to figure out how to implement them. Thanks!
I would do this:
array.zip(new_array).all? { |left, right| left != right }
Here are two approaches that do not involve repeated sampling until a valid sample is obtained:
Sample from the population of valid permutations
Construct the population from which you are sampling:
array = [1, 2, 3, 4]
population = array.permutation(array.size).reject do |a|
a.zip(array).any? { |e,f| e==f }
end
#=> [[2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [3, 1, 4, 2], [3, 4, 1, 2],
# [3, 4, 2, 1], [4, 1, 2, 3], [4, 3, 1, 2], [4, 3, 2, 1]]
Then just choose one at random:
10.times { p population.sample }
# [4, 3, 1, 2]
# [3, 4, 1, 2]
# [3, 4, 1, 2]
# [4, 3, 1, 2]
# [2, 1, 4, 3]
# [2, 1, 4, 3]
# [4, 1, 2, 3]
# [2, 1, 4, 3]
# [4, 3, 1, 2]
# [3, 4, 1, 2]
Sequentially sample for each position in the array
def sample_no_match(array)
a = array.each_index.to_a.shuffle
last_ndx = a[-1]
a.dup.map do |i|
if a.size == 2 && a[-1] == last_ndx
select = a[-1]
else
select = (a-[i]).sample
end
a.delete(select)
array[select]
end
end
10.times.each { p sample_no_match(array) }
# [2, 4, 3, 1]
# [4, 3, 1, 2]
# [2, 1, 3, 4]
# [1, 3, 4, 2]
# [1, 3, 2, 4]
# [1, 3, 2, 4]
# [1, 4, 3, 2]
# [3, 4, 2, 1]
# [1, 3, 4, 2]
# [1, 3, 4, 2]
I have been unable to prove or disprove that the second method produces a random sample. We can, however, determine relative frequencies of outcomes:
n = 500_000
h = n.times.with_object(Hash.new(0)) { |_,h| h[sample_no_match(array)] += 1 }
h.keys.each { |k| h[k] = (h[k]/(n.to_f)).round(4) }
h #=> {[1, 2, 3, 4]=>0.0418, [2, 1, 3, 4]=>0.0414, [1, 4, 2, 3]=>0.0418,
# [3, 4, 2, 1]=>0.0417, [4, 3, 2, 1]=>0.0415, [3, 1, 4, 2]=>0.0419,
# [2, 3, 1, 4]=>0.0420, [4, 2, 3, 1]=>0.0417, [3, 2, 1, 4]=>0.0413,
# [4, 2, 1, 3]=>0.0417, [2, 1, 4, 3]=>0.0419, [1, 3, 2, 4]=>0.0415,
# [1, 2, 4, 3]=>0.0418, [1, 3, 4, 2]=>0.0417, [2, 4, 1, 3]=>0.0414,
# [3, 4, 1, 2]=>0.0412, [1, 4, 3, 2]=>0.0423, [4, 1, 3, 2]=>0.0411,
# [3, 2, 4, 1]=>0.0411, [2, 4, 3, 1]=>0.0418, [3, 1, 2, 4]=>0.0419,
# [4, 3, 1, 2]=>0.0412, [4, 1, 2, 3]=>0.0421, [2, 3, 4, 1]=>0.0421}
avg = (h.values.reduce(:+)/h.size.to_f).round(4)
#=> 0.0417
mn, mx = h.values.minmax
#=> [0.0411, 0.0423]
([avg-mn,mx-avg].max/avg).round(6)
#=> 0.014388
which means that the maximum deviation from the average was only 1.4% percent of the average.
This suggests that the second method is a reasonable way of producing pseudo-random samples.
Initially, the first line of this method was:
a = array.each_index.to_a
By looking at the frequency distribution for outcomes, however, it was clear that that method did not produce a pseudo-random sample; hence, the need to shuffle a.
Here's one possibility:
until array.zip(new_array).reject{ |x, y| x == y }.size == array.size
new_array = array.shuffle
end
Note, though, that it will break for arrays like [1] or [1, 1, 1, 2, 3], where the number of instances of 1 exceeds half the size of the array. Recommend Array#uniq or similar, along with checking for arrays of sizes 0 or 1, depending on how trustworthy your input is!

How to modify an array subset in Ruby iteratively?

I know the idiomatic way to do a for loop in Ruby is to use an Enumerator like .each, but I'm running into a problem: I'd like to iterate over a subset of an Array and modify those elements. Calling .map! with a subset like ary[0..2] or .slice(0..2) doesn't seem to do it; presumably because that slicing operator is creating a new Array?
Desired behavior with for instead of iterator:
iter_ind = [2,3,4]
my_ary = [1,3,5,7,9,11]
for j in iter_ind
my_ary[j] = my_ary[j] + 1
# some other stuff like an exchange operation maybe
end
=> [1, 3, 6, 8, 10, 11]
Things that don't work:
irb(main):032:0> ar[2..4].map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):033:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):034:0> ar.slice(2..4).map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):035:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):036:0> ar[2..4].collect! {|el| el = el+1}
=> [6, 8, 10]
irb(main):037:0> ar
=> [1, 3, 5, 7, 9, 11]
Try this.
In example below I implemented something that could be named map_with_index. each_with_index if no block given returns iterator. I use it to map our array.
ary = [1, 3, 5, 7, 9, 11]
ary.each_with_index.map { |elem, index| index.between?(2, 4) ? elem += 1 : elem }
# => [1, 3, 6, 8, 10, 11]
You may also try the following:
?> ary = [1, 3, 5, 7, 9, 11]
=> [1, 3, 5, 7, 9, 11]
?> ary.map!.with_index {|item, index| index.between?(2, 4) ? item += 1 : item}
=> [1, 3, 6, 8, 10, 11]
?> ary
=> [1, 3, 6, 8, 10, 11]
You could use Array#each_index if you don't mind referencing the array twice:
ary = [1, 3, 5, 7, 9, 11]
ary.each_index { |i| ary[i] += 1 if i.between? 2, 4 }
#=> [1, 3, 6, 8, 10, 11]
Or if you don't want to iterate the whole array, this would work, too:
ary = [1, 3, 5, 7, 9, 11]
ary[2..4] = ary[2..4].map { |el| el + 1 }
ary
#=> [1, 3, 6, 8, 10, 11]

Resources