I am struggling to build a small ruby snippets to compare two arrays and conditionally replace items in one of the array.
I have a "book" model which has titles and titles have chapters. I have an array with all the lines of a book and want to replace the chapters by the corresponding title in this array.
def replace_chapters_by_titles(all_lines_of_a_book)
books = Book.all
all_lines_of_a_book.each do |line|
books.each do |chapter|
if (line =~ /#{book.chapter}/)
line = "#{book.title}" #this is where I am not sure what I should do
end
end
end
end
I guess this has no impact on the array as I am just putting "#{book.title}" in line without pushing anything to the array "all_lines_of_a_book". Could someone help me find the right syntax?
You need to push to the index where line exists in array, try below code
def replace_chapters_by_titles(all_lines_of_a_book)
books = Book.all
all_lines_of_a_book.each_with_index do |line, index| # note this
books.each do |chapter|
if (line =~ /#{book.chapter}/)
all_lines_of_a_book[index] = "#{book.title}" # and this
end
end
end
all_lines_of_a_book # probably you want to return new array
end
This approach may help:
arr # => [1, 22, 5, 66, 77, 8, 88, 0]
subst # => [9, 8, 7, 6, 5, 4, 3, 2]
cond = lambda { |x| x>10 } # condition for substitution
arr.zip(arr.map(&cond)).each_with_index.map do |(a,b),i|
if b then subst[i] else a end
end # => [1, 8, 5, 6, 5, 8, 3, 0]
arr # => [1, 22, 5, 66, 77, 8, 88, 0]
subst # => [9, 8, 7, 6, 5, 4, 3, 2]
arr.each_index.map { |i| arr[i] > 10 ? subst[i] : arr[i] }
# => [1, 8, 5, 6, 5, 8, 3, 0]
or
arr.each_with_index.map { |n,i| n > 10 ? subst[i] : n }
Related
How can I ensure uniqueness in this array while maintaining its length at 5?
def fixed
5.times.collect { SecureRandom.random_number(10) }
end
This behaviour seems odd:
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 3]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3]
When the number of possible values is small – like 10 in your example – then I would generate an array with all options and just pick a random sample of entries:
(0..9).to_a.sample(5)
If the number of possible values is huge then generation all values first is certainly not an option. Then I would generate a random value as long as the array doesn't contain enough entries:
require 'set'
values = Set.new
until values.length == 5 do
values.add(SecureRandom.random_number(1_000_000))
end
values.to_a
Note the I am using a Set to ensure the uniqueness of the values in the second version.
Using SecureRandom
def fixed
unique_numbers = []
5.times.collect do
loop do
number = SecureRandom.random_number(10)
break number unless unique_numbers.include?(number)
end
end
end
And if you want to generate unique numbers between 1 to 10, then you can create array of 1 to 10 and use shuffle or sample to get random numbers.
Using shuffle
> (0...10).to_a.shuffle.take(5)
=> [4, 0, 1, 3, 7]
> (0...10).to_a.shuffle.take(5)
=> [6, 2, 3, 9, 1]
> (0...10).to_a.shuffle.take(5)
=> [9, 2, 5, 8, 4]
> (0...10).to_a.shuffle.take(5)
=> [5, 0, 6, 8, 7]
> (0...10).to_a.shuffle.take(5)
=> [2, 7, 1, 5, 0]
Using sample
> (1..10).to_a.sample(5)
=> [4, 6, 3, 2, 7]
> (1..10).to_a.sample(5)
=> [5, 8, 2, 3, 7]
> (1..10).to_a.sample(5)
=> [2, 5, 6, 1, 3]
> (1..10).to_a.sample(5)
=> [8, 5, 10, 9, 3]
> (1..10).to_a.sample(5)
=> [8, 1, 5, 3, 4]
You can also pass SecureRandom custom random generator as an argument with sample
> (1..10).to_a.sample(5, random: SecureRandom)
=> [6, 3, 4, 7, 10]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [7, 4, 8, 1, 5]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [8, 3, 9, 5, 10]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [6, 8, 9, 2, 1]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [9, 10, 1, 8, 2]
Just out of curiosity, using Enumerable#cycle infinite generator.
MAX = 10
SIZE = 5
[MAX].cycle.inject(Set.new) do |acc, max|
break acc if acc.size >= SIZE
acc << SecureRandom.random_number(max)
end
#⇒ #<Set: {2, 1, 7, 0, 9}>
or even with generic loop:
loop.each_with_object(Set.new) do |_, acc|
break acc if acc.size >= SIZE
acc << SecureRandom.random_number(10)
end
#⇒ #<Set: {2, 6, 7, 1, 3}>
One way would be to generate a range of numbers from 0 to 10 and
then shuffle them to get the unique random numbers.
You can convert that range to Array using to_a and shuffle them using shuffle
You can do something like this:
(0..10).to_a.shuffle[0..4] # => [8, 6, 1, 9, 10]
[0..4] will give you the first 5 shuffled elements.
From
a = [1, 2, 3, 4, 5, 6]
I create array with
a.permutation(5).to_a
=> [[1, 2, 3, 4, 5], [1, 2, 3, 4, 6],.......
How can I parse the array so I can create records assigning the index of array to the Model.attribute. Something like.....
for_each do
Model.create(n1: 1, n2: 2, n3: 3, n4: 4, n5: 5)
end
thanks!
You can try to use Array#map for this:
arrays = a.permutation(5).to_a
arrays.each do |array|
attribs = array.map.with_index { |item, i| ["n#{i + 1}", item] }.to_h
Model.create(attribs)
end
I am woking on the solution for the following question.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
This is the solution submitted in ruby after referring the C++ code http://leetcodeunlock.com/2016/05/20/leetcode-1-two-sum-easy/ .
def two_sum(nums, target)
hash = {}
arr = []
nums.each_with_index do |value,index|
y = target - value
if(hash.find{|key,val| key == value})
arr << hash[value]
arr << index
return arr
else
hash[y] = index
end
end
end
My submission failed with the message : Time limit exceeded. Can anyone point out the mistake and help me optimise the code?
nums = [2, 7, 11, 15]
target = 9
# this will find all combinations of 2 elements that add up to 9
results = (0...nums.size).to_a.combination(2).select { |first, last| nums[first] + nums[last] == target }
results.first #=> [0, 1]
Explanation of some parts of the code:
# Get indexes of all elements of nums array
(0...nums.size).to_a #=> [0, 1, 2, 3]
# Generate all combinations of indexes of each 2 elements
(0...nums.size).to_a.combination(2).to_a #=> [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
I have modified the line
if(hash.find{|key,val| key == value})
to
if(hash.key?(value))
to find if a specific key is present in the hash and this solved the issue.
Code
def sum_to_num(arr, num)
return [num/2, num/2] if num.even? && arr.count(num/2) > 1
a = arr.uniq.
group_by { |n| (2*n-num).abs }.
find { |_,a| a.size > 1 }
a.nil? ? nil : a.last
end
This method requires three or four passes through the array, if num is even, one to count the instances of num/2, one to remove duplicate values, one to group_by and one to find the pair of numbers that sum to the desired total. It therefore should be much faster than methods that evaluate every pair of the array's elements, particularly as the size of the array is increased.
Examples
sum_to_num [2, 11, 7, 15], 9
#=> [2, 7]
sum_to_num [2, 5, 2, 6, 1, -5, 4], 10
#=> [6, 4]
sum_to_num [2, 7, 11, -7, 15], 0
#=> [7, -7]
sum_to_num [2, 7, 11, 7, 15], 14 #???
sum_to_num [2, -7, 11, -7, 15], -14 #???
sum_to_num [2, 7, 11, 15], 17
#=> [2, 15]
sum_to_num [2, -11, 8, 15], 4
#=> [-11, 15]
sum_to_num [2, -11, 8, 15], -3
#=> [-11, 8]
sum_to_num [2, -11, 8, 15], 100
#=> nil
Explanation
Assume x and y sum to num. Then
2*x-num + 2*y-num = 2*(x+y) - 2*num
= 2*num - 2*num
= 0
meaning that 2*x-num and 2*y-num are either both zero or they have the opposite signs and the same absolute value. Similarly, if 2*x-num and 2*y-num sum to zero, then
2*x-num + 2*y-num = 0
2*(x+y) - 2*num = 0
meaning that n+m = num (which is hardly surprising considering that 2*x+num is a linear transformation.
Suppose
arr = [2, 5, 2, 6, 1, -5, 4]
num = 10
then
if num.even? && arr.count(num/2) > 1
#=> if 10.even? && arr.count(5) > 1
#=> if true && false
#=> false
Therefore, do not return [5,5].
b = arr.uniq
#=> [2, 5, 6, 1, -5, 4]
c = b.group_by { |n| (2*n-num).abs }
#=> {6=>[2], 0=>[5], 2=>[6, 4], 8=>[1], 20=>[-5]}
a = c.find { |_,a| a.size > 1 }
#=> [2, [6, 4]]
return nil if a.nil?
# do not return
a.last
#=> [6, 4]
I was doing this challenge for fun and wrote a cleaned up ruby solution.
def two_sum(nums, target)
hash = {}
nums.each_with_index { |number, index| hash[number] = index }
nums.each_with_index do |number, index|
difference = target - number
if hash[difference] && hash[difference] != index
return [index, hash[difference]]
end
end
end
# #param {Integer[]} nums
# #param {Integer} target
# #return {Integer[]}
def two_sum(nums, target)
length = nums.length
for i in 0..length
j = i+1
for a in j..length
if j < length
if nums[i] + nums[a] == target
return [i, a]
end
end
j+=1
end
end
[]
end
Well this is my way of solving this
def two_sum(nums, target)
nums.each_with_index do |value, index|
match_index = nums.find_index(target - value)
return [index, match_index] if match_index
end
nil
end
The above has the advantage that it stops execution when a match is found and so hopefully won't time out. :)
Imagine the following Ruby array:
[9, 9, 5, 5, 5, 2, 9, 9]
What's the easiest way of removing redundant tuples, producing an output like the following:
[9, 5, 2, 9]
uniq is not correct because it's examining the entire array. The ordering of the input is important and must be kept. Is there a straightforward approach to this?
Thanks!
I'd do using Enumerable#chunk
2.0.0-p0 :001 > a = [9, 9, 5, 5, 5, 2, 9, 9]
=> [9, 9, 5, 5, 5, 2, 9, 9]
2.0.0-p0 :002 > a.chunk { |e| e }.map(&:first)
=> [9, 5, 2, 9]
I would do it like
b = [];
a.each { |n| b << n if b.last != n }
and b is the result
only one array scan is needed
I like Arup's answer best, but in case you want a method that is compatible with versions that don't have chunk you can do
a = [9, 9, 5, 5, 5, 2, 9, 9]
a.inject([a[0]]) { |b,c| b.last == c ? b : b << c }
# => [9, 5, 2, 9]
This is my version:
a.each_with_object([]) { |el, arr| arr << el if arr.last != el }
#=> [9, 5, 2, 9]
For those who land on this question looking to remove "redundant" values, the OP is trying to remove "repeated consecutive" values, not "redundant" or "duplicate" values and used the wrong word. They are different situations.
For clarification, removing redundant or duplicate values would be:
asdf = [9, 9, 5, 5, 5, 2, 9, 9]
asdf.uniq # => [9, 5, 2]
Or:
asdf & asdf # => [9, 5, 2]
Or:
require 'set'
asdf.to_set.to_a # => [9, 5, 2]
And, yes, I know the OP is asking for a different result. This is to show the answer for the question that was asked, NOT what what would meet the desired output. For that see the selected answer.
This is to show how you could use an enumerator directly, with the methods Enumerator#next and Enumerator#peek.
def purge_conseq_dups(arr)
return arr if arr.empty?
enum = arr.to_enum
a = []
loop do
e = enum.next
a << e unless e == enum.peek
end
a << arr.last
end
asdf = [9, 9, 5, 5, 5, 2, 9, 9]
purge_conseq_dups(asdf) #=> [9, 5, 2, 9]
When e is the last element of the enumerator enum, enum.peek raises a StopInteration exception which is rescued by Kernel#loop, which responds by breaking out of the loop. At that point all that remains is to append the last element of arr to a.
We could write a << e rather than a << arr.last, provided we initialize e prior to the loop (e.g., e = nil) so that the variable will be in scope in the last line.
# this code works
list = (0..20).to_a
# => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
odd = list.select { |x| x.odd? }
# => [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
list.reject! { |x| x.odd? }
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# but can i emulate this type of functionality with an enumerable method?
set = [1,5,10]
# => [1, 5, 10]
one, five, ten = set
# => [1, 5, 10]
one
# => 1
five
# => 5
ten
# => 10
# ------------------------------------------------
# method I am looking for ?
list = (0..20).to_a
odd, even = list.select_with_reject { |x| x.odd? }
# put the matching items into the first variable
# and the non-matching into the second
Sure, you can do:
odd, even = list.partition &:odd?
odd = []
even = []
list = [1..20]
list.each {|x| x.odd? ? odd << x : even << x }
As pguardiario said, the partition method is the most direct way. You could also use Set#divide:
require 'set'
list = (1..10).to_a
odd, even = Set.new(list).divide(&:odd?).to_a.map{|x| x.to_a}
You could try below:
odd,even = (0..20).group_by(&:odd?).values
p odd,even
Output:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]