# How to change one element in a ring

Hi,
How to change a given element in a ring? Example:
notes = (scale :e3, :minor_pentatonic)
I would like to change say the third element for the note :r.
notes[2] = :r #generates an error

Patrick

Rings are immutable, so the solution is to build a fresh string.

``````notes = scale :e3,:minor_pentatonic
puts notes # ===> (ring 52, 55, 57, 59, 62, 64)
notes2 = notes[0..1]+[:r]+notes[3..-1]
notes=notes2
puts notes #==> (ring 52, 55, :r, :r, 62, 64)
``````

A function which can do this for you is shown below.
NB the position to replace is numbered from 0 upwards (0 gives is first value in the ring)

``````notes = scale :e3,:minor_pentatonic

puts notes # ==> (ring 52, 55, 57, 59, 62, 64)

define :update do |n,position,val|
pos =[0, [position, n.length - 1].min].max #set limits to position
#puts pos #for debugging
l=pos-1;r=pos+1 #calc slice positions
#puts "l,r",l,r #for debugging
if l < 0 #special case. Insert val at start of ring
return [val]+n[1..-1]
elsif
r > n.length - 1 #special case insert val at end of ring
return n[0..-2]+[val]
else
return n[0..l]+[val]+n[r..-1] #normal cases
end
end

notes = update(notes,3,:r)

puts notes # ==> (ring 52, 55, 57, :r, 62, 64)
``````

In this example the FOURTH note is replaced counting from 0 this is 0,1,2,3
If you want you can adjust things to count from 1 by inserting line

``````position -= 1
``````

as the first line in the definition

EDIT the min max methods are used to clamp position so that it is not outside the ring. Otherwise
if you put something like update(notes,400,:r) it would give an error. With the error checking this will replace the last character in the ring.

EDIT 2
you may be able to write this a bit more nicely by using the .take and .take_last methods listed in the Sonic Pi tutorial section shown below

http://sonic-pi.net/tutorial.html#section-8-5

Thank you @robin.newman!
Regards,
Patrick

I’ve now answered my last edit. Here is a simpler version using the methods in section 8.5 of the tutorial

``````define :update do |n,position,val|
puts position #for debugging
if position < 1 #special case. Insert val at start of ring
return [val]+n.drop(1)
elsif
position >= n.length #special case insert val at end of ring
return n.drop_last(1)+[val]
else
return n.take(position-1)+[val]+n.take_last(n.length-position) #normal cases
end
end

notes = scale :e3,:minor_pentatonic
puts notes # ===> (ring 52, 55, 57, 59, 62, 64)

puts update( notes,2,:r) # => (ring 52, :r, 57, 59, 62, 64)
``````