concatenate with splat ruby - ruby

say I have
arr = [1,2,3]
How can I change this method so it adds each argument to the array?
def add(*number)
arr << *number
end
So add(4,5,6) produces:
arr #=> [1,2,3,4,5,6]

When accepting arguments via splat, they will always be an array. So you can simply add the two arrays together.
def add(*numbers)
arr + numbers
end

Use concat:
def add(*nums)
arr.concat nums
end
Or +:
def add(*nums)
arr + nums
end

$arr = [1,2,3]
def add(*number)
$arr.concat number
end
add(4,5,6)
$arr #=> [1,2,3,4,5,6]
Note: concat modifies the object it operates on ($arr). Plus (+) does not.
As the Tin Man mentions, you don't want to use a global to do this. It is better to simply do
arr.concat [4,5,6]
outside of a function call. Better yet:
arr += [4,5,6]

Related

Ruby Map Method edits the original array?

def removal(arr)
letters ="i"
p arr
new_array = arr.map do |c_word|
c_word.each_char.with_index do |char, index|
if letters.include?(char)
c_word[index] = "*"
end
end
end
p arr #the original array is getting edited? why?
p new_array
end
removal(["hiiiiiigh","git", "training"])
In this code, the original array (arr) in the map method keeps getting edited. I thought that map does not edit the original array. If I needed to edit the original, then I would use .map!
I believe it has something to do with the nested enumerator or a variable reference that I am not seeing. Instead of each_char.with_index, I used a while loop and map would still edit the original array. Why is the original array being edited?
You are actually wrong in (at least) two places:
map is not editing the original array
in fact, the original array is not being edited at all
If you look closely, the array hasn't changed, only the strings inside the array have changed. And it is not map that is doing this, it is String#[]=, which you are calling here:
c_word[index] = "*"
So, you are calling a method that edits strings, and you should not be surprised that your strings are edited!
Think of using:
map as saying "I want to create new data based on existing data"
each as saying "I either want to not change any data, or change existing data"
Having this in mind, what you are doing is using map with array to create new array based on existing one, and then using each to modify characters in existing strings. This is why the strings in the original array end up modified.
To fix this use map twice, first to "create new array based on existing array", and then the second time to "create new string based on existing string". This way the original strings won't get modified.
def removal(arr)
letters ="i"
p arr
new_array = arr.map do |word|
word.chars.map do |char|
letters.include?(char) ? '*' : char
end.join
end
p arr
p new_array
end
removal(["hiiiiiigh","git", "training"]) #=> ["hiiiiiigh", "git", "training"]
# ["hiiiiiigh", "git", "training"]
# ["h******gh", "g*t", "tra*n*ng"]
More practical solution to this problem would be something like this:
def censor(strings, forbidden_chars_string, censor_char = '*')
re = Regexp.union(forbidden_chars_string.chars)
strings.map {|str| str.gsub(re, censor_char) }
end
p ["hiiiiiigh","git", "training"] #=> ["hiiiiiigh", "git", "training"]
p censor(["hiiiiiigh","git", "training"], "i") #=> ["h******gh", "g*t", "tra*n*ng"]
p censor(["hiiiiiigh","git", "training"], "gn", '_') #=> ["hiiiiii_h", "_it", "trai_i__"]
This is happening because inside the map block you are doing some processing on each word of arr and not on each word new_array. If you want to copy the words of arr and change it in new_array then create a copy, change it and return the word.
Checkout these 2 codes and you will get my point
Code 1
def removal(arr)
letters ="i"
p arr
new_array = arr.map do |c_word|
c_word.each_char.with_index do |char, index|
if letters.include?(char)
c_word[index] = "*"
end
end
c_word
end
p arr
p new_array
end
removal(["hiiiiiigh","git", "training"])
Here you are changing words of arr and copying it to new_array
Code 2
def removal(arr)
letters ="i"
p arr
new_array = arr.map do |c_word|
n_word = c_word.dup
n_word.each_char.with_index do |char, index|
if letters.include?(char)
n_word[index] = "*"
end
end
n_word
end
p arr
p new_array
end
removal(["hiiiiiigh","git", "training"])
Here you are copying words of arr, changing it and adding them to new_array
If you don't want to change the array's elements, you should not change them. Your problem is in this line:
c_word[index] = "*"
So just use methods that do not affect the recipient, e.g.:
def removal(array)
letter = 'i'
array.map { |word| word.gsub(letter, '*') }
end

array.each return the same unchanged array after being modified.

Is there a way to return the new array?
I try to return the array of squared value [1, 4, 9] but it keeps returning [1, 2, 3] (the original array) Here is my code:
def square_array(array)
array.each do |number|
number *= number
puts number
end
end
square_array([1, 2, 3])
A much simpler version that does what you want:
def square_array(array)
array.map do |number|
number*number
end
end
The problem with your code is that when you assign something to number, you're just assigning a value to a local variable, not some magic reference into an array.
Try this out:
Using Each method
def square_array(array)
temp = []
array.each do |number|
temp << (number * number)
end
temp
end
When writing semantic ruby, it is best to use proper enumerable methods. There is no need for the temporary variable in this case... we can use the #map method to return a new array that is the result of applying a function to each value in turn. This is a core concept of the functional programming paradigm:
def square_array numbers
numbers.map { |x| x ** 2 }
end
I fund a way, I just needed to push the result in a new array
def square_array(array)
new_array = []
array.each do |num|
squared_num = num ** 2
new_array.push(squared_num)
end
return new_array
end

Iterate through a part of enumerator(external iterator)?

If I want a part of an array I can use [] or split:
arr = [1,2,3,4,5]
arr[1..3]
=> [2, 3, 4]
But is there a 'general' version of []? Can I apply it to any Enumerator?
enum = arr.each
enum.xxx_method(1..3) # is equal to arr[1..3].each
Of course you can use arr[1..3] directly. But I'm seeking a general way to handle any enumerator.
If you have an enumerator, you can count on Enumerable methods drop and take:
# abstract if necessary as enum_slice(range)
enumerator.drop(2).take(3)
If that enumerator is an array you don't need to traverse it, check the method Array#lazy_slice that I asked to be added to enumerable_lazy in relation with your previous question:
require 'enumerable/lazy'
class Array
def lazy_slice(range)
Enumerator.new do |yielder|
range.each do |index|
yielder << self[index]
end
end.lazy
end
end
some_big_array = (0..10000).to_a # fake array, it won't generally be a range
p some_big_array.lazy_slice(9995..10000).map { |x| 2*x }.to_a
#=> [19990, 19992, 19994, 19996, 19998, 20000]

How would you implement this idiom in ruby?

As someone who came from Java background and being a newbie to Ruby,
I was wondering if there is a simple way of doing this with ruby.
new_values = foo(bar)
if new_values
if arr
arr << new_values
else
arr = new_values
end
end
Assuming "arr" is either an array or nil, I would use:
arr ||= []
arr << new_values
If you're doing this in a loop or some such, there might be more idiomatic ways to do it. For example, if you're iterating a list, passing each value to foo(), and constructing an array of results, you could just use:
arr = bars.map {|bar| foo(bar) }
If I'm understanding you correctly, I would probably do:
# Start with an empty array if it hasn't already been set
#arr ||= []
# Add the values to the array as elements
#arr.concat foo(bar)
If you use #arr << values you are adding the entire array of values to the end of the array as a single nested entry.
arr = [*arr.to_a + [*new_values.to_a]]
Start with:
arr ||= []
And then, depending on whether new_values is an array or not
arr += new_values # if array
arr << new_values # if not
arr += [*new_values] # if it could be either
Furthermore, you can get rid of the test on new_values by taking advantage of the fact that NilClass implements a .to_a => [] method and reduce everything to:
arry ||= []
arr += [*new_values.to_a]
But wait, we can use that trick again and make the entire thing into a one-liner:
arr = [*arr.to_a + [*new_values.to_a]]
I don't intend to write an inexcrutable one-liner, but I think this is quite clear. Assuming, as Phrogz, that what you really need is an extend (concat):
arr = (arr || []).concat(foo(bar) || [])
Or:
(arr ||= []).concat(foo(bar) || [])
I would use:
new_values = foo(bar)
arr ||= []
arr << new_values if new_values

How can I programmatically pass args to yield in Ruby?

How can I pass a variable number of args to a yield.
I don't want to pass an array (as the following code does), I'd actually like to pass them as a programmatic number of args to the block.
def each_with_attributes(attributes, &block)
results[:matches].each_with_index do |match, index|
yield self[index], attributes.collect { |attribute| (match[:attributes][attribute] || match[:attributes]["##{attribute}"]) }
end
end
Use the splat-operator * to turn the array into arguments.
block.call(*array)
or
yield *array
Use the asterisk to expand an array into its individual components in an argument list:
def print_num_args(*a)
puts a.size
end
array = [1, 2, 3]
print_num_args(array);
print_num_args(*array);
Will print:
1
3
In the first case the array is passed, in the second case each element is passed separately. Note that the function being called needs to handle variable arguments such as the print_num_args does, if it expects a fixed size argument list and received more or less than expected you will get an exception.
Asterisk will expand an array to individual arguments in ruby:
def test(a, b)
puts "#{a} + #{b} = #{a + b}"
end
args = [1, 2]
test *args

Resources