Ruby array - how can I make values ​​persist over nil values

I am working with an array of midi steps that looks like this ...

pitches = [ 60, nil, nil, nil, 67, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] 

In this case, the step is still 60 at indices 1, 2, and 3.

Following index 4, step another 67.

How can I write a method to determine the previous value other than zero?

The only way I can do it now seems a bit awkward:

 def pitch_at_step(pitches,step) if pitches.any? x = pitches[step] until x != nil index -= 1 x = pitches[step] end x else nil end end 

The expected output is in the format:

 pitch_at_step(pitches, 0) # 60 pitch_at_step(pitches, 2) # 60 pitch_at_step(pitches, 4) # 67 pitch_at_step(pitches, 8) # 67 

Is this the best solution? Is there a more accurate and / or more efficient way?

+6
source share
7 answers

If the array is small, you can use something like this:

 pitches[0..index].compact.last 

It seems tidier, but it's not as good as yours for large datasets

+4
source
 pitches.slice_before(&:itself).flat_map{|a| a.fill(a.first)} # => [60, 60, 60, 60, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, # 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67] 
+3
source

Here is a simple way to build a converted array:

 last = pitches.first pitches[1..-1].map { |i| curr = i || last; last = curr; curr } .unshift(pitches.first) #=> [60, 60, 60, 60, 67,... 67] 

The OP did not say whether the first pitches element is pitches non-zero. Let be:

 pitches = [nil, nil, 61, nil, nil, 60, nil] 

The above method will return:

 [nil, nil, 61, 61, 61, 60, 60] 

what we want. Some other answers stumble when pitches[step] = nil and pitches[i] = nil for all i < step ( step is the index of the given pitches element).

+2
source

If you have large arrays that are basically nil , why not use a hash instead, save only non-zero values? And you look at the keys. (here is an unoptimized version)

 pitches = {0 => 60, 4 => 67} def find(idx) lower = pitches.keys.select { |i| i <= idx} return pitches[lower.sort.last] end 

If performance is a problem, you can track sorted keys.

+2
source

To get one value:

  value = index.downto(0){|i| pitches[i] and break pitches[i] } 

To calculate all values:

  values = (y = nil) || pitches.map{|x| x ? y = x : y } 
+1
source

This can also be achieved with Enumerable#chunk :

 tmp = nil pitches.chunk {|el| tmp = el unless el.nil?; tmp }. map {|val, ar| [val] * ar.size }.flatten # => [60, 60, 60, 60, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, # 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67] 
+1
source

I think this is a good way to do this in Ruby:

 pitches[0..index].reverse_each.find{|x|x} 
0
source

Source: https://habr.com/ru/post/981180/


All Articles