replacing an element in nested array ruby
class BingoBoard
def initialize(board)
@bingo_board = board
end
def number_letter
@letter = ['B','I','N','G','O'].sample
@number = rand(1..100)
end
def checker
number_letter
@bingo_board.each do |n|
index = n.index(@number)
n[index] = 'X' unless index.nil?
end
end
end
How to replace element in multidimensional array in ruby
This is the root of the problem:
@bingo_board.collect! { |i| (i == @ball) ? "X" : i}
In this example, i is an array. So what you might want to do is to replace your code with something like:
@bingo_board.collect! do |i| # you're iterating over a double array here
if i.include?(@ball) # i is a single array, so we're checking if the ball number is included
i[i.index(@ball)] = 'X'; i # find the index of the included element, replace with X
else
i
end
end
Or if you prefer one-liner:
@bingo_board.collect! { |i| i.include?(@ball) ? (i[i.index(@ball)] = 'X'; i) : i }
Be aware that this is going to only replace the first occurrence of the element. So, say if your ball is 10, and you have:
[8, 9, 9, 10, 10]
you will get:
[8, 9, 9, "X", 10]
If you want ALL of the 10s to be replaced, then do something like:
@bingo_board.collect! do |i|
if i.include?(@ball)
i.collect! { |x| x == @ball ? 'X' : x }
else
i
end
end
Ruby: replacing a matching element in a multidimensional array?
Let me know if this works out (tried to reduce the variations from 1 to 10 in order to be able to test easier):
class Lotto
def initialize
@lotto_slip = Array.new(5) {Array(6.times.map{rand(1..10)})}
end
def current_pick
@number = rand(1..10)
puts "The number is #{@number}."
end
def has_number
#prints out initial slip
@lotto_slip.each {|x| p x}
#Prints slip with an "X" replacing number if is on slip
#Ex: @number equals 4th number on slip --> 1, 2, 3, X, 5, 6
@lotto_slip.each do |z|
if z.include?(@number)
p "#{@number} included in #{z}"
z.map! { |x| x == @number ? 'X' : x}
end
end
@lotto_slip
end
end
test = Lotto.new
test.current_pick
p test.has_number
The problems I saw with your code are:
You don't need the
to_s
for this line@number = rand(1..60).to_s
, else how are you going to compare the numbers produced by the array with an actual string?You need to re-generate the array instead of re-assigning, that's why I've replaced all of that code with
z.map! { |x| x == @number ? 'X' : x}
which basically re-generates the entire array.
Find and replace in a Ruby multi dimensional array
Following sepp2k idea, here is a possible implementation:
class Object
def deep_map(&block)
if self.respond_to? :each
result = []
self.each do |e|
result << e.deep_map(&block)
end
return result
else
return block.call(self)
end
end
end
Then apply deep_map as you wish on the array:
> [[[3, 3, 5], [4, 3, 3]], [[3, 2, 3], [0, 3, 8]]].deep_map { |e| e > 3 ? 0 : e }
=> [[[3, 3, 0], [0, 3, 3]], [[3, 2, 3], [0, 3, 0]]]
Or, more briefly:
class Object
def deep_map(&block)
respond_to?(:map) ? map{|e| e.deep_map(&block)} : block.call(self)
end
end
Or, polymorphically:
class Object
def deep_map(&block); block.call(self) end
end
class Array
def deep_map(&block); map{|e| e.deep_map(&block)} end
end
How to convert a part of nested arrays
Looping over an array with each
does not change it. Even if you override the loop variable (rec_upd_date
) it does not change the actual value.
In order to mutate an array you should use map
:
csv = csv.map do |rec_upd_date|
"#{rec_upd_date.slice(8,2)}/#{rec_upd_date.slice(5,2)}/#{rec_upd_date.slice(0,4)}"
end
The return value of each iteration will be the value of the respective item in the resulting array.
or, you can shorten this with the change in-place version of map!
:
csv.map! do |rec_upd_date|
"#{rec_upd_date.slice(8,2)}/#{rec_upd_date.slice(5,2)}/#{rec_upd_date.slice(0,4)}"
end
For example:
csv = ["2015-03-20 10:17:20 +0200", "2015-04-14 10:17:20 +0200", "2013-12-24 10:17:20 +0200"]
csv = csv.map do |rec_upd_date|
"#{rec_upd_date.slice(8,2)}/#{rec_upd_date.slice(5,2)}/#{rec_upd_date.slice(0,4)}"
end
# => ["20/03/2015", "14/04/2015", "24/12/2013"]
For an input like:
[["03/20/2015", A, B],["06/03/2015", C, D],
["04/22/2015", E, F],["03/20/2015", A2, B2],
["06/03/2015", C2, D2],["01/03/2015", E2, F2],
["12/20/2015", A3, B3],["11/05/2015", C3, D3],
["07/15/2015", E3, F3]]
and you want to change only the first element of each item:
[["20/03/2015", A, B],["03/06/2015", C, D],
["22/04/2015", E, F], ...]
the simplest solution will be to return an array for each iteration:
csv.map do |arr|
[do_something_to(arr[0]), arr[1], arr[2]]
end
Another option is, to set the first element of each array item. Since you are not trying to replace the item, but only to modify it, using each
will work:
csv.each do |arr|
arr[0] = do_something_to(arr[0])
end
Setting the value of an element within a nested array
I figured it out. When I initialized @board
with Array.new(10, Array.new(10))
, it created an Array of 10 identical Arrays. That is, each Array had the same object_id.
@board[0].object_id
=> 22148328
@board[1].object_id
=> 22148328
I solved the issue by using the map
method:
@board = Array.new(10).map{ |x| Array.new(10) }
replace values of an array within range
You can use Array#fill
method.
array.fill(7, 8..100)
Ruby dynamically updating value in nested array
Your iteration is almost there, you just need to stop one step before you run through path
so that you can have the array you need to modify rather than the element.
So split the path
into the pieces you want:
*p, target = path
# [3], 1
Then use #inject
to find the array:
ary = p.inject(example) { |i, a| a[i] }
# [3, 4]
and then do your assignment:
ary[target] = 9
Of course you'll need to add some logic to deal with the unexpected such as path
leading you to a non-array element or path
not matching the structure of example
(consider path = [11, 6, 23]
in your example).
You could also use #dig
instead of #inject
:
ary = example.dig(*p)
ary[target] = 9
# or
example.dig(*p)[target] = 9
That would take care of some of the problematic path
s and you'd be left with deciding what to do if ary.nil?
.
Related Topics
Rspec: How to Write Unit Test Case to Receive an Exception Which Is Getting Raised in Private Method
When Passing a Ruby Array as an Argument, Why Does '<<' Append While '+=' Does Not
Importing CSV as Test Data in Cucumber
Bug in Implemented Tagging System
Method Gives Activerecord::Relation Error
What Is a Very Simple Authentication Scheme for Sinatra/Rack
Rspec Controller Testing - Blank Response.Body
My Classes Can't Use Shoes Methods Like Para
Cannot Install Ruby 1.9.3 on a Clean Lion Install
Rails + Google Calendar API Events Not Created
Create and Initialize Instances of a Class with Sequential Names
How to Share Image and Description Using Social_Share_Button in Rails
Rails 3 - Devise/Actionmailer/Ruby-Smtp Causing a Segmentation Fault