Rajagopalan
Rajagopalan

Reputation: 6064

How to replace this nil value in Ruby style

I have an array like

 A=[[a,x,y,z],[nil,e,f,d],[nil,k,j,u],[nil,w,p,k],[b,x,y,z],[nil,e,f,d],[nil,k,j,u],[nil,w,p,k]]

Result

 A=[[a,x,y,z],[a,e,f,d],[a,k,j,u],[a,w,p,k],[b,x,y,z],[b,e,f,d],[b,k,j,u],[b,w,p,k]]

You might be noticing that first character of the first array is replacing the upcoming nil value until first character of the first array is b and then nil values are replaced with b.

This one I have achieved through iterating the array and writing the if condition and storing the first character somewhere and then by comparing the first character while I am iterating. But I don't still don't find a ruby way of doing this. Can someone help me here

Upvotes: 1

Views: 133

Answers (2)

steenslag
steenslag

Reputation: 80065

A = [['a',1,2],[nil,3,4],[nil,5,6],[nil,7,8],['b',9,10],[nil,11,12],[nil,13,14]]

A.each_cons(2){|first, last| last[0] ||= first[0] }

last[0] ||= first[0] means something like "Give last[0] the value of first[0], unless it already has a value (not nil or false)".

Upvotes: 3

Cary Swoveland
Cary Swoveland

Reputation: 110675

arr = [['a',1,2],[nil,3,4],[nil,5,6],[nil,7,8],['b',9,10],[nil,11,12],[nil,13,14]]

last = nil
arr.map do |f,*rest|
  if f.nil?
    [last, *rest]
  else
    last = f
    [f, *rest]
  end
end
  #=> [["a", 1, 2], ["a", 3, 4], ["a", 5, 6], ["a", 7, 8],
  #    ["b", 9, 10], ["b", 11, 12], ["b", 13, 14]] 

The steps are as follows:

last = nil
enum = arr.map
  #=> #<Enumerator: [["a", 1, 2], [nil, 3, 4], [nil, 5, 6], [nil, 7, 8],
  #                  ["b", 9, 10], [nil, 11, 12], [nil, 13, 14]]:map> 
f,*rest = enum.next
  #=> ["a", 1, 2]
f #=> "a"
rest
  #=> [1, 2] 
f.nil?
  #=> false 
last = f
  #=> "a" 
[f, *rest]
  #=> ["a", 1, 2] 

f,*rest = enum.next
  #=> [nil, 3, 4] 
f.nil?
  #=> true 
[last, *rest]
  #=> ["a", 3, 4] 

f,*rest = enum.next
  #=> [nil, 5, 6] 
f.nil?
  #=> true 
[last, *rest]
  #=> ["a", 5, 6] 

f,*rest = enum.next
  #=> [nil, 7, 8] 
f.nil?
  #=> true 
[last, *rest]
  #=> ["a", 7, 8] 

and so on.

Alternatively,

arr.drop(1).each_with_object([arr.first]) { |a,ar|
  ar << [a.first || ar.last.first, *a.drop(1)] }
  #=> [["a", 1, 2], ["a", 3, 4], ["a", 5, 6], ["a", 7, 8],
  #    ["b", 9, 10], ["b", 11, 12], ["b", 13, 14]] 

Upvotes: 2

Related Questions