What does * mean at the beginning of a range? [duplicate] - arrays

This question already has answers here:
Ruby * operator before array [duplicate]
(2 answers)
Closed 3 years ago.
Sorry for the noob question, but what does this asterisk mean at the beginning of a range?
class Matrix
def initialize(matrix_string)
#matrix = matrix_string.split("\n").map do |row|
row.split.map(&:to_i)
end
#rows = rows.length
#cols = columns.length
end
def rows
#matrix
end
def columns
#matrix.transpose
end
# --->>***
def saddle_points
[*0...#rows].product([*0...#cols]).select do |coords|
saddle_point?(*coords)
end
# ***<----
end
private
def saddle_point?(row, col)
(#matrix[row][col] == rows[row].max) &&
(#matrix[row][col] == columns[col].min)
end
end

As it is stated in the documentation:
You can turn an Array into an argument list with * (or splat) operator:
arguments = [1, 2, 3]
my_method(*arguments)
The same might be done for Range:
arguments = 1..3
my_method(*arguments) # essentially the same as my_method(1, 2, 3)
Also splat operator is allowed before ranges inside the array declaration to implicitly convert Range to Array:
[*1..3]
#⇒ [1, 2, 3]

* unpacks an array by "splatting" its contents and converting it to an array.

As stated by the other answers, the * operator is used to turn an array into an argument list.
But what if the object is not an array, as in your case? Then Ruby will call #to_a on the object, if defined, and use the returned array instead. Otherwise, the object itself is used.
Here's the case where the object is neither an array nor does it define #to_a:
x = *4 # => [4]
If the object defines #to_a, it will be called and used, as in the case of Range:
x = *0..1 # => [0, 1]
To prove this, we can prepend a module to trace the call to #to_a:
module Trace
def to_a
puts "#to_a called"
super
end
end
Range.prepend(Trace)
x = *0..1
# prints "#to_a called"
# => [0, 1]
Note that Ruby will not call #to_a if your object already is of type Array.
We can use this on our custom types as well:
class Foo
def to_a
[1, 2, 3]
end
end
x = *Foo.new # => [1, 2, 3]
By the way, Ruby's nil also implements #to_a. This allows us to pass nil as an argument as if nothing was passed, because nil.to_a returns []:
def count(*args)
args.count
end
count # => 0
count(*0..1) # => 2
count(*nil) # => 0
This can be useful when you have a variable that could be nil that you pass to a method which has a default value:
def say_hi(name = "Jane")
puts "Hi, #{name}"
end
name = nil
say_hi(*name) # prints "Hi, Jane"
But if we remove NilClass#to_a:
NilClass.undef_method(:to_a)
say_hi(*name) # prints "Hi, "

Related

Getting nil error when adding elements in Array - Ruby

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

Array interpreted as a Fixnum

I'm currently learning ruby and I wrote this piece of code :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr.first + rec_sum(num_arr.shift)
end
end
puts rec_sum(multi_gen)
That should return the sum of all 3 and 5 multiples up to 1000.
But I get an error :
myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
from villani.rb:17:in `rec_sum'
from villani.rb:21:in `<main>'
But when I re-write it like this :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr[0] + rec_sum(num_arr[1..num_arr.last])
end
end
puts rec_sum(multi_gen)
I don't get the error.
So why is my first rec_sum functions interpretting my Array as a Fixnum in the first case?
The issue is in the recursive call:
rec_sum(num_arr.shift)
Array#shift returns the shifted element, not the remaining array. You should explicitly pass the array as an argument to recursive call:
rec_sum(num_arr[1..-1])
or
rec_sum(num_arr.tap(&:shift))
The latter would [likely] be looking too cumbersome for the beginner, but it’s a very common rubyish approach: Object#tap yields the receiver to the block, returning the receiver. Inside a block (num_arr.tap(&:shift) is a shorthand for num_arr.tap { |a| a.shift } we mutate the array by shifting the element out, and it’s being returned as a result.
mudasobwa already explained why using shift doesn't give the expected result. Apart from that, your code is somehow unidiomatic.
In multi_gen you are creating an empty array and append elements to it using a for loop. You rarely have to populate an array manually. Instead, you can usually use one of Ruby's Array or Enumerable methods to generate the array. select is a very common one – it returns an array containing the elements for which the given block returns true:
(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]
In rec_sum, you check if num_arr == []. Although this works, you are creating an empty throw-away array. To determine whether an array is empty, you should call its empty?:
if num_arr.empty?
# ...
end
To get the remaining elements from the array, you use:
num_arr[1..num_arr.last]
which can be abbreviated by passing a negative index to []:
num_arr[1..-1]
There's also drop which might look a little nicer:
num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))
Another option to get first and remaining elements from an array is Ruby's array decomposition feature (note the *):
def rec_sum(num_arr)
if num_arr.empty?
0
else
first, *remaining = num_arr
first + rec_sum(remaining)
end
end
You could also consider using a guard clause to return from the method early:
def rec_sum(num_arr)
return 0 if num_arr.empty?
first, *remaining = num_arr
first + rec_sum(remaining)
end
Writing recursive methods is great for learning purposed, but Ruby also has a built-in sum method:
multi_gen.sum #=> 234168
or – since you are using an older Ruby version – inject:
multi_gen.inject(0, :+) #=> 234168

arr.delete() vs arr.delete_at() in Ruby

I wrote the function below which accepts an array and returns a randomized version of it.
I've noticed that I sometimes end up with a nil element in randomizedArr when using list.delete(element) to remove an element from the array, but this does not happen when using list.delete_at(index) -- note that the latter is commented out in the below snippet. Am I missing something?
If there's a better way to do what I'm trying to achieve with this function then I would appreciate any suggestion. Thanks!
The array I'm passing to this function is a string array with ~2k elements. I'm passing in a clone of the original array so it doesn't become empty when the function is called. I'm using Ruby 2.1 on Windows 7.
def getRandomList(list)
randomizedArr = Array.new()
cnt = list.length
while (cnt >= 1) do
index = rand(cnt)
prod = list[index]
randomizedArr.push(prod)
list.delete(prod)
#list.delete_at(index)
cnt = cnt - 1
end
if randomizedArr.include?(nil)
puts "found nil element"
end
return randomizedArr
end #getRandomList()
I am not sure why you need to put all that logic when you can randomize the list by list.shuffle.
Refering to the Ruby documentation this is what I found to answer your question...
#To delete an element at a particular index:
arr = [2, 3, 4, 5] #I added this bit
arr.delete_at(2) #=> 4
arr #=> [2, 3, 5]
#To delete a particular element anywhere in an array, use delete:
arr = [1, 2, 2, 3]
arr.delete(2) #=> 2
arr #=> [1,3]
All of that can be found here https://ruby-doc.org/core-2.4.1/Array.html
arr.delete(2) will remove any instance of 2 in an array while delete_at(2) only removes the third value in the array.

Ruby modify array items and return full array

I have this code here
string.split(/(\w{1,}=)/).each_slice(1).map { |i| items << i }
items.map! do |i|
i = i << str if i.to_s =~ /\w{1,}=/
end
puts items*''
And I want to modify certain items in the array based on regex, then return the full array with the modified items in it. This only returns the modified items. How do I achieve what I'm looking for?
EDIT: Ok, so say I'm trying to split a link using this regex:
page.php?site=blah&id=1
The link is split and added to the array which now contains
page.php?
site=
blah&
id=
1
What I want to do is append some value to the end of the elements ending with a =. This way, when I return the modified array as a string it would output like this:
page.php?site=(newval)&id=(newval)
You have several undefined variables in your example, which is very sloppy.
each_slice(1) is equivalent to each(), so it's not clear why you are using each_slice(1). In any case, both each() and map() step through the items in an Array one by one, but each() returns the original Array unchanged. On the other hand, you use map() when you want to create a new Array that contains changes to the items.
In the regex /\w{1,}/, there is a shortcut for the quantifier {1, }, and it's: +, so most people would write the regex as /\w+/, where + means 1 or more.
I want to modify certain items in the array based on regex, then
return the full array with the modified items in it.
Here is an example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
else
num - 1
end
end
p results
--output:--
[0, 6, 2]
Your current attempt with map() doesn't return anything if the conditional fails. Note how the example above returns something both when the condition fails AND when the condition succeeds. map() replaces an item with whatever is returned for that item.
Now look at this example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
end
end
p results
--output:--
[nil, 6, nil]
If you don't return something for an item, then map() will use nil for that item. In the example, if the condition num == 2 is true then num+4 is returned--but if num == 2 is false, nothing is returned.
Edit:
words = %w[
page.php?
site=
blah&
id=
1
] #=> words = ["page.php?", "site=", "blah&", "id=", "1"]
suffix = 'hello'
results = words.map do |word|
if word.end_with?('=')
"#{word}#{suffix}"
else
word
end
end
p results
--output:--
["page.php?", "site=hello", "blah&", "id=hello", "1"]
Instead of parsing a URL with a regex, have you considered using the addressable gem?
require 'addressable/uri'
uri = Addressable::URI.parse('page.php?site=blah&id=1&bar')
uri.query_values = uri.query_values.map do |k, v|
[k, v.is_a?(String) ? v << 'foo' : v]
end
puts uri.to_s # => page.php?site=blahfoo&id=1foo&bar
This won't handle very complex query parameters (it will just pass them through).
You can use respond_to? :sub! and v.sub! /$/, 'foo' instead of checking types if that makes you uneasy. (I wouldn't use :<< or :concat because those are valid methods for Arrays.)

Intersection of an arbitrary number of arrays

I need a function which takes an array of arrays as its argument, then returns the intersection of all the subarrays. How could I improve the following code, if at all?
class Array
def grand_intersection
if self.length > 1
filters = self[1..-1]
filters.reduce(self[0]) {|start, filter| start & filter}
else
self
end
end
end
P.S. I'm not too concerned about handling arrays whose content won't respond to #& -- the method won't be exposed to the user.
class Array
def grand_intersection
self.reduce :&
end
end
[[1,2,3,4,5], [2,3,4], [1,2,4,5]].grand_intersection
#=> [2, 4]

Resources