How to iterate the ruby array without negative indices - arrays

I need to compare each value in ruby array with previous and next value.
Update
Example:
[1,2,4,5]
I want to check like this. (a[i] with a[i-1] and a[i+1])
1 with only next value # as there is no prev value
2 with prev & next value
4 with prev & next value
5 with only prev value # as there is no next value
In ruby, the a[-1] is not pointing to nil, it is taking last value. So, unable to iterate. Is there any alternate solution?
Tried
changing array to [nil,1,2,4,5,nil]
but getting following error
comparison of Fixnum with nil failed (ArgumentError)
instead of 0..n I tried 1...n. but this does not solve my issue.
Question:
How to ignore negative indices for first(i-1) and last(i+1) element in ruby array.

Your comparison doesn't really make sense. You are comparing everything twice, but if someone really is changing the array while you are iterating over it, you have much bigger problems than this (and you still will not catch modifications made to the beginning of the array when you are already in the middle). It is enough to compare each consecutive pair of elements, which is easily done:
[1, 2, 4, 5].each_cons(2).all? {|a, b| a < b }
If you really absolutely MUST compare triples, that is also easily done:
[1, 2, 4, 5].each_cons(3).all? {|a, b, c| a < b && b < c }
And if you want to make the size of the sliding window generic, then you can do something like this:
[1, 2, 4, 5].each_cons(n).all? {|window|
window.each_cons(2).map {|a, b| a < b }.inject(:&)
}

I need to compare each value in ruby array with previous and next
value.
This method takes an array and a comparison method such as :<, :> or :== etc
def prev_next arr, com
arr.map.with_index { |e,i|
if i == 0
[ e,
e.send(com,arr[i.succ])
]
elsif i == arr.length-1
[ e.send(com,arr[i.pred]),
e
]
else
[ e.send(com,arr[i.pred]),
e,
e.send(com,arr[i.succ])
]
end
}
end
arr = [1,2,3,4,5]
p prev_next(arr,:<)
#=> [[1, true], [false, 2, true], [false, 3, true], [false, 4, true], [false, 5]]
Note the second parameter can be passed as a string or a symbol because send is clever enough to convert strings to symbols.
Notable methods: Object#send, Fixnum#succ and Integer#pred

Now I totally agree with Jörg here that each_cons is the way to go and you should probably look for some other structure of the data if comparing the data is this complicated.
With that said. Nothing prevents normal index lookups in Ruby, and if nothing else works just implement your requirements in a simple case statement:
my_array = [1,2,4,5]
my_array.size.times do |ix|
case ix
when 0 then my_array[ix] == my_array[ix+1]
when my_array.size-1 then my_array[ix] == my_array[ix-1]
else my_array[ix-1] == my_array[ix] == my_array[ix+1]
end
end

Related

Ruby - compare two arrays for index matches and with the remainder if included

Working on a project to recreate a game Mastermind. I need to compare two arrays, and running into some struggles.
I need to output two integers for the flow of the game to work,
the first integer is the number of correct choices where the index matches. The code I have for this appears to be working
pairs = #code.zip(guess)
correct_position_count = pairs.select { |pair| pair[0] == pair[1] }.count
Where pairs is equal to a 4 element array and the guess is also a 4 element array
The second part I am having a bit of trouble with on how to do the comparison and return an array. The integer should represent where the two arrays index don't match (the above code block but !=) and confirm whether the guess array excluding any exact index matches has any elements included with the code array once again excluding the exact index matches.
Any help would be greatly appreciated!
I am not completely sure to understand your problem but if I understood well, you've two arrays, solution with the solution and guess with the current guess of the player.
Now, let's assume that the solution is 1234 and that the guess is 3335.
solution = [1, 2, 3, 4]
guess = [3, 3, 3, 5]
an element by element comparison produces an array of booleans.
diff = guess.map.with_index { |x,i| x == solution[i] }
# = [false, false, true, false]
Now, you can easily compute the number of good digits diff.count true and the number of wrong digits diff.count false. And, in case you need the index of the false and/or true values you can do
diff.each_index.select { |i| diff[i] } # indexes with true
# = [2]
diff.each_index.select { |i| !diff[i] } # indexes with false
# = [0, 1, 3]
You can count all digit matches ignoring their positions and then subtract exact matches.
pairs = #code.zip(guess)
correct_position_count = pairs.select { |pair| pair[0] == pair[1]}.count
any_position_count = 0
code_digits = #code.clone # protect #code from modifying
guess.each do |digit|
if code_digits.include?(digit)
code_digits.delete_at(code_digits.find_index(digit)) # delete the found digit not to count it more than once
any_position_count += 1
end
end
inexact_position_count = any_position_count - correct_position_count
puts "The first value: #{correct_position_count}"
puts "The second value: #{inexact_position_count}"

What's the cleanest way to construct a Ruby array using a while loop?

Ruby has lots of nice ways of iterating and directly returning that result. This mostly involve array methods. For example:
def ten_times_tables
(1..5).map { |i| i * 10 }
end
ten_times_tables # => [10, 20, 30, 40, 50]
However, I sometimes want to iterate using while and directly return the resulting array. For example, the contents of the array may depend on the expected final value or some accumulator, or even on conditions outside of our control.
A (contrived) example might look like:
def fibonacci_up_to(max_number)
sequence = [1, 1]
while sequence.last < max_number
sequence << sequence[-2..-1].reduce(:+)
end
sequence
end
fibonacci_up_to(5) # => [1, 1, 2, 3, 5]
To me, this sort of approach feels quite "un-Ruby". The fact that I construct, name, and later return an array feels like an anti-pattern. So far, the best I can come up with is using tap, but it still feels quite icky (and quite nested):
def fibonacci_up_to(max_number)
[1, 1].tap do |sequence|
while sequence.last < max_number
sequence << sequence[-2..-1].reduce(:+)
end
end
end
Does anyone else have any cleverer solutions to this sort of problem?
Something you might want to look into for situations like this (though maybe your contrived example fits this a lot better than your actual use case) is creating an Enumerator, so your contrived example becomes:
From the docs for initialize:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
and then call it:
p fib.take_while { |elem| elem <= 5 }
#=> [1, 1, 2, 3, 5]
So, you create an enumerator which iterates all your values and then once you have that, you can iterate through it and collect the values you want for your array in any of the usual Ruby-ish ways
Similar to Simple Lime's Enumerator solution, you can write a method that wraps itself in an Enumerator:
def fibonacci_up_to(max_number)
return enum_for(__callee__, max_number) unless block_given?
a = b = 1
while a <= max_number
yield a
a, b = b, a + b
end
end
fibonacci_up_to(5).to_a # => [1, 1, 2, 3, 5]
This achieves the same result as returning an Enumerator instance from a method, but it looks a bit nicer and you can use the yield keyword instead of a yielder block variable. It also lets you do neat things like:
fibonacci_up_to(5) do |i|
# ..
end

Ruby - Efficient method of checking if sum of two numbers in array equal a value

Here's my problem: I have a list of 28,123 numbers I need to iterate through and an array of 6965 other numbers checking if the sum of two numbers (can be the same number) have equal value to each of the 28,123 numbers. I want to put them in a new array or mark them as true / false. Any solutions I've come up with so far are extremely inefficient.
So a dumbed-down version of what I want is if I have the following: array = [1, 2, 5] and the numbers 1 to 5 would return result = [2, 3, 4] or the array of result = [false, true, true, true, false]
I read this SE question: Check if the sum of two different numbers in an array equal a variable number? but I need something more efficient in my case it seems, or maybe a different approach to the problem. It also doesn't seem to work for two of the same number being added together.
Any help is much appreciated!
non_abundant(n) is a function that returns the first n non_abundant numbers. It executes almost instantaneously.
My Code:
def contains_pair?(array, n)
!!array.combination(2).detect { |a, b| a + b == n }
end
result = []
array = non_abundant(6965)
(1..28123).each do |n|
if array.index(n) == nil
index = array.length - 1
else
index = array.index(n)
end
puts n
if contains_pair?( array.take(index), n)
result << n
end
end
numbers = [1, 2, 5]
results = (1..10).to_a
numbers_set = numbers.each_with_object({}){ |i, h| h[i] = true }
results.select do |item|
numbers.detect do |num|
numbers_set[item - num]
end
end
#=> [2, 3, 4, 6, 7, 10]
You can add some optimizations by sorting your numbers and checking if num is bigger then item/2.
The complexity is O(n*m) where n and m are lengths of two lists.
Another optimization is if numbers list length is less then results list (n << m) you can achieve O(n*n) complexity by calculating all possible sums in numbers list first.
The most inefficient part of your algorithm is the fact that you are re-calculating many possible sums of combinations, 28123 times. You only need to do this once.
Here is a very simple improvement to your code:
array = non_abundant(6965)
combination_sums = array.combination(2).map {|comb| comb.inject(:+)}.uniq
result = (1..28123).select do |n|
combination_sums.include? n
end
The rest of your algorithm seems to be an attempt to compensate for that original performance mistake of re-calculating the sums - which is no longer needed.
There are further optimisations you could potentially make, such as using a binary search. But I'm guessing this improvement will already be sufficient for your needs.

Ruby: Sorting an Array, skipping the first element

I want to sort an Array of Arrays of Strings by the first String skipping the first Array but I just don't have an idea how to do it using the build-in sort method. I could copy the whole array without the first element and sort the resutling Array then but isn't there a more elegant way to do this?
ar = [["zzzz", "skip", "this"], ["EFP3","eins","eins"], ["EFP10","zwei","zwei"], ["EFP1","drei","drei"]]
ar.sort!{ |a,b|
if a == ar.first # why doesn't
next # this
end # work ?
# compare length, otherwise it would be e.g. 10 < 3
if a[0].length == b[0].length
a[0] <=> b[0]
else
a[0].length <=> b[0].length
end
}
I want to have the result like this:
["zzzz", "skip", "this"], ["EFP1","drei","drei"], ["EFP3","eins","eins"], ["EFP10","zwei","zwei"]
sortet by "EFP#"
edit: I'm using Ruby 1.8, if it matters.
ar[1..-1].sort { whatever you want }
You can do it this way:
[ar.first] + ar[1..-1].sort{ |a,b| a[0] <=> b[0] }
# => [["zzzz", "skip", "this"], ["EFP1", "drei", "drei"], ["EFP10", "zwei", "zwei"], ["EFP3", "eins", "eins"]]
but isn't there a more elegant way to do this?
You could sort the other elements and re-assign them:
ar = [5, 4, 3, 2, 1]
ar[1..-1] = ar[1..-1].sort
ar #=> [5, 1, 2, 3, 4]
I want to have the result [...] sortet by "EFP#"
sort_by looks like the right tool:
ar = [["zzzz", "skip"], ["EFP3", "eins"], ["EFP10", "zwei"], ["EFP1", "drei"]]
ar[1..-1] = ar[1..-1].sort_by { |s, _| s[/\d+/].to_i }
ar #=> [["zzzz", "skip"], ["EFP1", "drei"], ["EFP3", "eins"], ["EFP10", "zwei"]]
s[/\d+/].to_i extracts the digits from s and converts it to an integer:
"EFP1"[/\d+/].to_i #=> 1
"EFP3"[/\d+/].to_i #=> 3
"EFP10"[/\d+/].to_i #=> 10
Others have explained how to get the right answer.
As for why it doesn't work, sort simply doesn't expect "next". "next" is a language construct intended for a normal loop. sort, however, is a function that repeatedly asks another function for a result. As a normal Ruby function, it can't detect if you returned "next" because that's the equivalent of returning nil (or leaving the body empty). And so, it can't have, and doesn't have, any conventions about how to handle a "next" instance.
It causes an error because nil is not a valid number to return from the |a,b| comparison.
The comparison returns -1, 0 or 1, so if you return 1 for the first it is sorted as the first element, as 1 it would become the last element.
ar.sort!{ |a,b|
if a == ar.first
-1
elsif a[0].length == b[0].length # compare length, otherwise it would be e.g. 10 < 3
a[0] <=> b[0]
else
a[0].length <=> b[0].length
end
}
#=>[["zzzz", "skip", "this"], ["EFP1", "drei", "drei"], ["EFP3", "eins", "eins"], ["EFP10", "zwei", "zwei"]]

How to find duplicates in array without using `uniq` method

I am doing a challenge to make a method that finds duplicate values in an array, and prints out a new array without the duplicates. Ruby has a built in uniq method; however, I am not allowed to use it.
In my mind, this should work:
def uniques(array)
tempPos = 0
arrayPos = 0
duplicate = true
result = [] # array the result will be "pushed" too
for arrayPos in 0..array.length
for tempPos in 0..array.length
# If the values at the indexes are the same. But the indexes are not the same.
# we have a duplicate
if array[arrayPos] == array[tempPos] && arrayPos != tempPos
duplicate = true
else
duplicate = false
end
if duplicate == false
result[arrayPos] = array[arrayPos]
end
end
puts duplicate
end
puts result.inspect
end
Output:
uniq *this is the short hand user input to run the method*
false
false
false
false
false
false
[1, 2, 1, 4, 5, nil]
I must be doing something wrong.
Are you allowed to use a Set?
require 'set'
array = [1, 2, 3, 3, 3, 4]
Set.new(array).to_a
#=> [1, 2, 3, 4]
An other way is to iterate over every pair in the array:
array.each_cons(2).with_object([array.first]) do |pair, result|
result << pair.last unless pair.first == pair.last
end
#=> [1, 2, 3, 4]
There are many ways to do that. Here's another. Suppose:
arr = [3,5,1,3,4,1,1]
Construct:
h = arr.group_by(&:itself)
#=> {3=>[3, 3], 5=>[5], 1=>[1, 1, 1], 4=>[4]}
The duplicates are given by:
h.select { |_,v| v.size > 1 }.keys
#=> [3, 1]
and an array without the duplicates is given by:
h.keys
#=> [3, 5, 1, 4]
Your logic works fine altough as mentioned above a set would work better. You could also sort the elements, and then find adjacent pairs that are the same value which wouldn't work as well as a set, but would have slightly better run-time than your current solution:
To polish what you currently have:
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = false
for tempPos in 0...result.length
# if the values at the indexes are the same... but the indexes are not the same...
# we have a duplicate
duplicate ||= (array[arrayPos] == result[tempPos])
end
if !duplicate
result << array[arrayPos]
end
end
puts result
end
an slightly better approach (altought still poor performance):
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = result.include?(array[arrayPos])
if !duplicate
result << array[arrayPos]
end
end
puts result
end
Although this solution is OK for a learning assignment, you should note that the complexity of this is O(n^2) (n-squared). What that means is that for an array of size n (for example n=10), you are doing n-squared (100) iterations.
It gets exponentially worse. If you have an array of length 1,000,000, you are doing 1,000,000,000,000 iterations. This is why using a set is so important, it's average run-time will be much lower.
A fairly simple way to so this is to leverage array.include?
new = []
arr.each { |x| new << x unless new.include?(x)}
puts new
That will give you an array (new) that only includes unique elements from the original array (arr)
Duplicate array easy way
arr1 = [1,3,4,5,6,6,6,1]
arry = Array.new(arr1)
puts arry
Find uniq array easy way using OR operator
arr1 = [1,3,4,5,6,6,6,1]
arr2 = Array.new # creating new array
arry = arr1 | arr2 # compare two array using OR operator
puts arry

Resources