Getting nil error when adding elements in Array - Ruby - arrays

I am trying to get a number from the user, store this number in the array and then add everything in the array together to display a total.
names = Array.new(20)
sum = 0
x = 0
for index in 0..5
puts "Enter a number: "
data = gets.to_i
names.push data
x = x + names[index]
end
puts x
But I am getting the error rb:10:in `+': nil can't be coerced into Integer (TypeError)
I guess its not allowing me to add whatever is in the array together. Anybody know a workaround for this?

There are some issues with your code.
Array.new(20) – you probably think this creates an array of the given size. Well, it does, but it also fills the array with a default object of nil:
Array.new(3)
#=> [nil, nil, nil]
In Ruby, you just create an empty array. It will grow and shrink automatically. And instead of Array.new you can use an array literal:
names = []
for index in 0..5 – for loops are very unidiomatic. You should use one of these:
(0..5).each do |index|
# ...
end
0.upto(5) do |index|
# ...
end
6.times do |index|
# ...
end
And finally:
names.push data
x = x + names[index]
You are pushing an element to the end of the array and then fetch it from the array using an absolute index. This only works if index and array size correlate exactly.
It's more robust to either use an explicit index for both, storing and fetching:
names[index] = data
x = x + names[index]
or to fetch the last element: (note that index isn't needed)
names.push data
x = x + names.last
Ruby also provides negative indices that are relative to the array's end:
names.push data
x = x + names[-1]
Needless to say, you could just omit the fetching:
names.push data
x = x + data
It might be useful to separate the data gathering from the calculation:
numbers = []
6.times do
puts 'Enter a number: '
numbers << gets.to_i
end
and then:
numbers = [1, 2, 3, 4, 5, 6] # <- example input
numbers.sum
#=> 21
# -- or --
numbers.inject(:+)
#=> 21
# -- or --
sum = 0
numbers.each { |n| sum += n }
sum
#=> 21

You are initializing the array with 20 nil names. Then you push the first entered number to the array (position 21) and try to concat the position [0] (names[0]) that is nil.
Try to change the first line:
names = Array.new

Related

How to square an array of numbers in ruby with while without `each` `map` or `collect` methods?

I'm new to coding in RUBY. I'm trying to write a method that squares each element in an array of numbers and returns a new array of these numbers squared. Trying to use while loop and NOT use each, collect, or map. Having trouble understanding how to index/loop each individual element of array and square is (**).
This is what makes sense to me but I know its wrong.
def square_array(numbers)
count = 0
while count < numbers.length do
numbers.index ** 2
end
square_array(numbers)
end
Will anyone please help me? Thanks!
The easy way to do it is map, of course:
def square_array(numbers)
numbers.map { |e| e ** 2 }
end
But here's what you have to do to do the same with a while loop (which is good practice).
Create an array to contain the transformed data.
Create a counter (you've done that).
Set up your while loop (as you have it, except you don't need the do at the end).
Write a statement that squares the array element whose index is the same as your counter, and pushes that result into the array you created in step 1.
Increment your counter by 1 (you forgot to do that, so you'll be getting an endless loop since count will always equal zero).
Return the array you created in step 1.
That will do it for you! See if you can put that together, rather than me just giving you the code.
def square_array(numbers)
# Allocate an array with the same size as `numbers`
# so that the runtime does not have to resize it from time to time
result = Array.new(numbers.size)
# The index
i = 0
while i < numbers.size
# Fill the result array
result[i] = numbers[i] ** 2
# and don't forget to increase the index,
# otherwise the loop will run forever.
i += 1
end
# Return the result array
result
end
The more functional approach would be to use recursion.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
And for any mapper (e. g. square root,) use it like:
fun.([1,2,3,4,5], ->(e) { e ** 2 }, fun)
#⇒ [1, 4, 9, 16, 25]
Please note! This approach mutates the initial array, so it should be explicitly array.dup’ed before passing to the function. To eliminate the necessity to pass the function itself through and leave the initial array intact, we would need a wrapper.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
mapper = ->(arr, map) { fun.([], arr.dup, map, fun) }
And use it like:
arr = [1,2,3,4,5]
mapper.(arr, ->(e) { e ** 2 })
#⇒ [1, 4, 9, 16, 25]
arr
#⇒ [1, 2, 3, 4, 5]
def sq(arr)
enum = arr.each
a = []
loop do
n = enum.next
a << n*n
end
a
end
sq [1, 2, 3, 4]
#=> [1, 4, 9, 16]
See Array#each, Kernel#loop and Enumerator#next. One could use Kernel#to_enum (documented in Object) in place of Array#each.
Using a for loop?
ary = [1,2,3]
res = []
for n in ary do
res << n ** 2
end
res
#=> [1, 4, 9]
But better you stick with map.
Here is my solution:
def square_array(numbers)
new_array = []
counter = 0
while counter < numbers.length()
new_array.push(numbers[counter] * numbers[counter])
counter += 1
end
return new_array
end
Without using each, map, or collect.
def square_array(array)
new_array = []
array.length.times do |index|
new_array.push(array[index] ** 2)
end
new_array
end

How to split a hash into multiple arrays of keys based the values not exceeding a certain sum in each array?

I have a large hash, where the keys are names, like "Alex", and the values are numeric, like "100".
How can I split this hash into multiple arrays that contain the keys, of which the sum of values doesn't exceed a certain threshold value?
Example
I have the hash
{"Alex"=>50, "Bamby"=>100, "Jordan"=>300, "Ger"=>700, "Aus"=>500, "Can"=>360}
and I want to split it into packs of 1000 from the beginning (doesn't have to be from the beginning but would be nice),
meaning:
array1 = ["Alex", "Bamby", "Jordan"] # not "Ger" bc it would exceed the 1000 in sum
array2 = ["Ger"] # not the Aus because it again would exceed the 1000
array3 = ["Aus", "Can"]
The best solution would actually be to have it optimized in a way that the code makes arrays all close or equal 1000 but that's the next step I guess...
Thank you so much in advance! ~Alex
h = {"Alex"=>50, "Bamby"=>100, "Jordan"=>300, "Ger"=>700, "Aus"=>500, "Can"=>360}
tot = 0
h.keys.slice_before { |k| (tot += h[k]) > 1000 ? tot = h[k] : false }.to_a
#=> [["Alex", "Bamby", "Jordan"], ["Ger"], ["Aus", "Can"]]
Not that if tot > 1000 the block returns a truthy value (h[k]) and the parentheses around tot += h[k] are necessary.
See Enumerable#slice_before.
original = {"Alex"=>50, "Bamby"=>100, "Jordan"=>300, "Ger"=>700, "Aus"=>500, "Can"=>360}
chunked = original.inject([]) do |array, (key, value)|
array << {} unless array.any?
if array.last.values.sum + value <= 1_000
array.last.merge!(key => value)
else
array << { key => value }
end
array
end
# => [{"Alex"=>50, "Bamby"=>100, "Jordan"=>300}, {"Ger"=>700}, {"Aus"=>500, "Can"=>360}]
You can iterate over the elements inside the hash like this, the explain is in the comments:
hash={"Alex"=>50, "Bamby"=>100, "Jordan"=>300, "Ger"=>700, "Aus"=>500, "Can"=>360}
rs = [] # the outside array
rss = [] # the array inside the array
m = 0 # check if the sum of nexts are 1000
hash.each do |key, n|
if m+n <= 1000 # if the counter + the next element < 1000
m += n # then add it to the counter
rss << key # add the key to the actual array
else
rs << rss #else m is equal or bigger than 1000, so, I add all the keys to the main array
m=n # the element that overcomes m to 1000, becomes the first count now
rss=[key] # And that key is the first element of a new array
end
end
rs << rss #Importan! at the end, the final array need to be added outside the loop
print rs
Result _
=> [["Alex", "Bamby", "Jordan"], ["Ger"], ["Aus", "Can"]]

Using memoization for storing values in ruby array

For a short array the following function works well. It's supposed to return the first array pair that whe sum is equal to a given integer. However, if the array has a length upwards of 10 million elements, the request times out, because (I think) is storing thousands of values in the variable I create in the first line. I know I have to use memoization (||=) but have no idea how to use it.
array1 = [1,2,3,4,5,6,7]
number = 3
array2 = [1,2,3.....n] # millions of elements
combos = array1.combination(2).to_a
(combos.select { |x,y| x + y == number }).sort.first
I need to gather all possible pairs to sort them, I'm using select to go through the entire list and not stop at the first pair that returns true.
This is one of the possible solutions.
def sum_pairs(ints, s)
seen = {}
for i in ints do
return [s-i, i] if seen[s-i]
seen[i] = true
end
nil
end
def find_smallest(arr, nbr)
first, *rest = arr.sort
until rest.empty?
matching = rest.bsearch { |n| n == nbr - first }
return [first, matching] unless matching.nil?
first, *rest = rest
end
nil
end
arr = [12, 7, 4, 5, 14, 9]
find_smallest(arr, 19) #=> [5, 14]
find_smallest(arr, 20) #=> nil
I've used the method Array#bsearch (rather than Enumerable#find to speed up the search for an element equal to nbr - first (O(log rest.size) vs. O(rest.size)).

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

I want to return the string that has the highest sum in an array for Ruby

So far I am able to return the sums of the strings:
puts "Please enter strings of #'s to find the number that has the greatest sum."
puts "Use commas to separate #'s."
user_input = gets.chomp
array = user_input.split(",")
array.map do |num_string|
num_string.chars.map(&:to_i).inject(:+)
end
But I wish to return the string that adds up to the highest value. For instance: If I have array = ["123","324","644"] I need it to return the sums of each value so the result should be 6,9,and 14 respectively. Since 1+2+3=6 etc. I am this far but now I need to return "644" as the answer since it is the string that sums to the highest value.
I suggest you use Enumerable#max_by:
arr = ["123","324","644"]
arr.max_by { |s| s.each_char.reduce(0) { |t,c| t+c.to_i } }
#=> "644"
Let's see how this works. Enumerable#max_by computes a value for each element of arr and returns the element of arr whose computed value is greatest. The calculation of the value for each element is done by max_by's block.
enum0 = arr.max_by
#=> #<Enumerator: ["123", "324", "644"]:max_by>
You can see the three elements of this enumerator. Sometimes it's not so obvious, but you can always see what they are by converting the enumerator to an array:
enum0.to_a
#=> ["123", "324", "644"]
Elements of enum0 are passed to the block by the method Enumerator#each (which in turn calls Array#each). You would find that:
enum0.each { |s| s.each_char.reduce(0) { |t,c| t+c.to_i } }
returns "644".
The first element of the enumerator ("123") is passed to the block by each and assigned to the block variable s. We can simulate that with the method Enumerator#next:
s = enum0.next
#=> "123"
Within the block we have another enumerator:
enum1 = s.each_char
#=> #<Enumerator: "123":each_char>
enum1.to_a
#=> ["1", "2", "3"]
enum1.reduce(0) { |t,c| t+c.to_i }
#=> 6
This last statement is equivalent to:
0 # initial value of t
0 + "1".to_i #=> 1 (new value of t)
1 + "2".to_i #=> 3 (new value of t)
3 + "3".to_i #=> 6 (new value of t)
6 is then returned by reduce.
For the next element of enum0:
s = enum0.next
#=> "324"
s.each_char.reduce(0) { |t,c| t+c.to_i }
#=> 9
and for the last element enum0:
s = enum0.next
#=> "644"
s.each_char.reduce(0) { |t,c| t+c.to_i }
#=> 14
Since 14 is the largest integer in [6, 9, 14], max_by returns the last element of arr, "644".
yourArray = ["123","324","644"]
highest = -1
pos = -1
yourArray.each_with_index{ |str, i|
sum = str.split("").map(&:to_i).reduce(:+)
highest = [highest,sum].max
pos = i if highest == sum
}
puts "highest value is " + yourArray[pos]
lets look at each step, map lets us enumerate the array and returns a new array with whatever we return from map:
yourArray.map{ |str| ... }
Here we're taking our strings in the array and splitting them into arrays of ["1","2","3"] for example, then the to_i portion converts this to an array such as [1,2,3] then finally reduce gives us our sum:
sum = str.split("").map(&:to_i).reduce(:+)
Here we're keeping track throughout the loop of the highest sum we've seen so far:
highest = [highest,sum].max
If ever the current sum is the highest, store its position in the array
pos = i if highest = sum
finally, use the stored array position to print out whatever exists there in the original array (the positions line up):
puts "highest value is " + yourArray[pos]

Resources