Replace a single element in an array - ruby

I have an array with unique elements. Is there a way to replace a certain value in it with another value without using its index value?
Examples:
array = [1,2,3,4]
if array.include? 4
# "replace 4 with 'Z'"
end
array #=> [1,2,3,'Z']
hash = {"One" => [1,2,3,4]}
if hash["One"].include? 4
# "replace 4 with 'Z'"
end
hash #=> {"One" => [1,2,3,'Z']}

p array.map { |x| x == 4 ? 'Z' : x }
# => [1, 2, 3, 'Z']

You can do it as:
array[array.index(4)] = "Z"
If the element is not necessarily in the array, then
if i = array.index(4)
array[i] = "Z"
end

You can use Array#map
array = array.map do |e|
if e == 4
'Z'
else
e
end
end
to edit the array in place, rather than creating a new array, use Array#map!
If you have more than one thing you want to replace, you can use a hash to map old to new:
replacements = {
4 => 'Z',
5 => 'five',
}
array = array.map do |e|
replacements.fetch(e, e)
end
This make uses of a feature of Hash#fetch, where if the key is not found, the second argument is used as a default.

A very simple solution that assumes there will be no duplicates and that the order doesn't matter:
hash = { 'One' => [1, 2, 3, 4] }
hash['One'].instance_eval { push 'Z' if delete 4 }
instance_eval sets the value of self to the receiver (in this case, the array [1,2,3,4]) for the duration of the block passed to it.

Related

Is there a one liner or more efficient way to get this done?

Given a hash, for example:
hash = { "fish" => 2, "cat" => 3, "dog" => 1 }
I need to get:
A comma separated string for all the values, E.g. "2,3,1"
An integer that hold the total sum of the values, E.g. 6
My current code:
value_string = hash.map { |k,v| "#{v}"}.join(',')
sum = 0
hash.map { |k,v| sum += v}
You can do it like this:
hash.values.join(",") # => "2,3,1"
hash.values.inject(:+) # => 6
Here is a solution to compute both the values in single iteration (some sort of one liner)
r1,r2 = hash.reduce([nil, 0]){|m,(_,v)| [[m[0], v.to_s].compact.join(","),m[-1]+v]}
#=> ["2,3,1", 6]

Count Fixnums in Ruby array

I have a function with an arbitrary number of arguments. My function looks like this:
sum(n, *rest)
How do I iterate through the array rest so that it will check if an argument is a Fixnum, sum it if it is, and do nothing otherwise? For example:
sum(5,1,2,k,D,3)
# => 6
Do as below :
ary = rest.grep(Fixnum)
# return the summation of the array elements, if array is not empty
# 0, when array is empty or contains only one element as `0`.
ary.reduce(0,:+)
Look #reduce and grep .
Examples :
ary = [1,"2",{1 => 2 },8]
ary.grep(Fixnum).inject(0,:+) # => 9
ary = ["2",{1 => 2 },(1..2)]
ary.grep(Fixnum).inject(0,:+) # => 0
# counting the how many Fixnums are there
ary = [1,"2",{1 => 2 },8]
ary.grep(Fixnum).size # => 2
Another way (though I prefer #Arup's use of grep):
a.group_by(&:class)[Fixnum].reduce(0,:+)
a = [1, :cat, 3, 'dog']
b = a.group_by(&:class) #=> {Fixnum=>[1,3],Symbol=>[:cat],String=>["dog"]}
c = b[Fixnum] #=> [1,3]
reduce(0,:+) #=> 4
I have assumed that zero is to be returned if the array contains no Fixnums. If, in that case, nil is to be returned, change reduce(0,:+) to reduce(:+).

How to create two seperate arrays from one input?

DESCRIPTION:
The purpose of my code is to take in input of a sequence of R's and C's and to simply store each number that comes after the character in its proper array.
For Example: "The input format is as follows: R1C4R2C5
Column Array: [ 4, 5 ] Row Array: [1,2]
My problem is I am getting the output like this:
[" ", 1]
[" ", 4]
[" ", 2]
[" ", 5]
**How do i get all the Row integers following R in one array, and all the Column integers following C in another seperate array. I do not want to create multiple arrays, Rather just two.
Help!
CODE:
puts 'Please input: '
input = gets.chomp
word2 = input.scan(/.{1,2}/)
col = []
row = []
word2.each {|a| col.push(a.split(/C/)) if a.include? 'C' }
word2.each {|a| row.push(a.split(/R/)) if a.include? 'R' }
col.each do |num|
puts num.inspect
end
row.each do |num|
puts num.inspect
end
x = "R1C4R2C5"
col = []
row = []
x.chars.each_slice(2) { |u| u[0] == "R" ? row << u[1] : col << u[1] }
p col
p row
The main problem with your code is that you replicate operations for rows and columns. You want to write "DRY" code, which stands for "don't repeat yourself".
Starting with your code as the model, you can DRY it out by writing a method like this to extract the information you want from the input string, and invoke it once for rows and once for columns:
def doit(s, c)
...
end
Here s is the input string and c is the string "R" or "C". Within the method you want
to extract substrings that begin with the value of c and are followed by digits. Your decision to use String#scan was a good one, but you need a different regex:
def doit(s, c)
s.scan(/#{c}\d+/)
end
I'll explain the regex, but let's first try the method. Suppose the string is:
s = "R1C4R2C5"
Then
rows = doit(s, "R") #=> ["R1", "R2"]
cols = doit(s, "C") #=> ["C4", "C5"]
This is not quite what you want, but easily fixed. First, though, the regex. The regex first looks for a character #{c}. #{c} transforms the value of the variable c to a literal character, which in this case will be "R" or "C". \d+ means the character #{c} must be followed by one or more digits 0-9, as many as are present before the next non-digit (here a "R" or "C") or the end of the string.
Now let's fix the method:
def doit(s, c)
a = s.scan(/#{c}\d+/)
b = a.map {|str| str[1..-1]}
b.map(&:to_i)
end
rows = doit(s, "R") #=> [1, 2]
cols = doit(s, "C") #=> [4, 5]
Success! As before, a => ["R1", "R2"] if c => "R" and a =>["C4", "C5"] if c => "C". a.map {|str| str[1..-1]} maps each element of a into a string comprised of all characters but the first (e.g., "R12"[1..-1] => "12"), so we have b => ["1", "2"] or b =>["4", "5"]. We then apply map once again to convert those strings to their Fixnum equivalents. The expression b.map(&:to_i) is shorthand for
b.map {|str| str.to_i}
The last computed quantity is returned by the method, so if it is what you want, as it is here, there is no need for a return statement at the end.
This can be simplified, however, in a couple of ways. Firstly, we can combine the last two statements by dropping the last one and changing the one above to:
a.map {|str| str[1..-1].to_i}
which also gets rid of the local variable b. The second improvement is to "chain" the two remaining statements, which also rids us of the other temporary variable:
def doit(s, c)
s.scan(/#{c}\d+/).map { |str| str[1..-1].to_i }
end
This is typical Ruby code.
Notice that by doing it this way, there is no requirement for row and column references in the string to alternate, and the numeric values can have arbitrary numbers of digits.
Here's another way to do the same thing, that some may see as being more Ruby-like:
s.scan(/[RC]\d+/).each_with_object([[],[]]) {|n,(r,c)|
(n[0]=='R' ? r : c) << n[1..-1].to_i}
Here's what's happening. Suppose:
s = "R1C4R2C5R32R4C7R18C6C12"
Then
a = s.scan(/[RC]\d+/)
#=> ["R1", "C4", "R2", "C5", "R32", "R4", "C7", "R18", "C6", "C12"]
scan uses the regex /([RC]\d+)/ to extract substrings that begin with 'R' or 'C' followed by one or more digits up to the next letter or end of the string.
b = a.each_with_object([[],[]]) {|n,(r,c)|(n[0]=='R' ? r : c) << n[1..-1].to_i}
#=> [[1, 2, 32, 4, 18], [4, 5, 7, 6, 12]]
The row values are given by [1, 2, 32, 4, 18]; the column values by [4, 5, 7, 6, 12].
Enumerable#each_with_object (v1.9+) creates an array comprised of two empty arrays, [[],[]]. The first subarray will contain the row values, the second, the column values. These two subarrays are represented by the block variables r and c, respectively.
The first element of a is "R1". This is represented in the block by the variable n. Since
"R1"[0] #=> "R"
"R1"[1..-1] #=> "1"
we execute
r << "1".to_i #=> [1]
so now
[r,c] #=> [[1],[]]
The next element of a is "C4", so we will execute:
c << "4".to_i #=> [4]
so now
[r,c] #=> [[1],[4]]
and so on.
rows, cols = "R1C4R2C5".scan(/R(\d+)C(\d+)/).flatten.partition.with_index {|_, index| index.even? }
> rows
=> ["1", "2"]
> cols
=> ["4", "5"]
Or
rows = "R1C4R2C5".scan(/R(\d+)/).flatten
=> ["1", "2"]
cols = "R1C4R2C5".scan(/C(\d+)/).flatten
=> ["4", "5"]
And to fix your code use:
word2.each {|a| col.push(a.delete('C')) if a.include? 'C' }
word2.each {|a| row.push(a.delete('R')) if a.include? 'R' }

How to initialize an array in one step using Ruby?

I initialize an array this way:
array = Array.new
array << '1' << '2' << '3'
Is it possible to do that in one step? If so, how?
You can use an array literal:
array = [ '1', '2', '3' ]
You can also use a range:
array = ('1'..'3').to_a # parentheses are required
# or
array = *('1'..'3') # parentheses not required, but included for clarity
For arrays of whitespace-delimited strings, you can use Percent String syntax:
array = %w[ 1 2 3 ]
You can also pass a block to Array.new to determine what the value for each entry will be:
array = Array.new(3) { |i| (i+1).to_s }
Finally, although it doesn't produce the same array of three strings as the other answers above, note also that you can use enumerators in Ruby 1.8.7+ to create arrays; for example:
array = 1.step(17,3).to_a
#=> [1, 4, 7, 10, 13, 16]
Oneliner:
array = [] << 1 << 2 << 3 #this is for fixnums.
or
a = %w| 1 2 3 4 5 |
or
a = [*'1'..'3']
or
a = Array.new(3, '1')
or
a = Array[*'1'..'3']
Along with the above answers , you can do this too
=> [*'1'.."5"] #remember *
=> ["1", "2", "3", "4", "5"]
To prove There's More Than One Six Ways To Do It:
plus_1 = 1.method(:+)
Array.new(3, &plus_1) # => [1, 2, 3]
If 1.method(:+) wasn't possible, you could also do
plus_1 = Proc.new {|n| n + 1}
Array.new(3, &plus_1) # => [1, 2, 3]
Sure, it's overkill in this scenario, but if plus_1 was a really long expression, you might want to put it on a separate line from the array creation.
You can do
array = ['1', '2', '3']
As others have noted, you can also initialize an array with %w notation like so:
array = %w(1 2 3)
or
array = %w[1 2 3]
Please note that in both cases each element is a string, rather than an integer.
So if you want an array whose elements are integers, you should not wrap each element with apostrophes:
array_of_integers = [1, 2, 3]
Also, you don't need to put comma in between the elements (which is necessary when creating an array without this %w notation). If you do this (which I often did by mistake), as in:
wrong_array = %w(1, 2, 3)
its elements will be three strings ---- "1,", "2,", "3". So if you do:
puts wrong_array
the output will be:
1,
2,
3
=>nil
which is not what we want here.
Hope this helps to clarify the point!
To create such an array you could do:
array = ['1', '2', '3']
If you have an Array of strings, you can also initialize it like this:
array = %w{1 2 3}
just separate each element with any whitespace
You can initialize an array in one step by writing the elements in [] like this:
array = ['1', '2', '3']
You can simply do this with %w notation in ruby arrays.
array = %w(1 2 3)
It will add the array values 1,2,3 to the arrayand print out the output as ["1", "2", "3"]

Distinct difference between collect and each? [duplicate]

This question already has answers here:
what's different between each and collect method in Ruby [duplicate]
(7 answers)
Closed 8 years ago.
Using arrays what's the main difference between collect and each? Preference?
some = []
some.collect do {|x| puts x}
some.each do |x|
puts x
end
array = [] is a shortcut to define an array object (long form: array = Array.new)
Array#collect (and Array#map) return a new array based on the code passed in the block. Array#each performs an operation (defined by the block) on each element of the array.
I would use collect like this:
array = [1, 2, 3]
array2 = array.collect {|val| val + 1}
array.inspect # => "[1, 2, 3]"
array2.inspect # => "[2, 3, 4]"
And each like this:
array = [1, 2, 3]
array.each {|val| puts val + 1 }
# >> 2
# >> 3
# >> 4
array.inspect # => "[1, 2, 3]"
Hope this helps...
collect (or map) will "save" the return values of the do block in a new array and return it, example:
some = [1,2,3,10]
some_plus_one = some.collect {|x| x + 1}
# some_plus_one == [2,3,4,11]
each will only execute the do block for each item and wont save the return value.

Resources