Putting value in sorted array

I have an array that’s sorted from highest value to lowest. I also want
to insert a value in sort order, but only if the value doesn’t exist
already. I ended up with a solution (writing from memory here, it was on
another computer) that feels a bit “long” for Ruby code, going something
like this:


arr = [5, 3, 1]
insert_value = 2

arr.each_index {|v, i|
break if v == insert_value
if insert_value > v
arr.insert(i, insert_value)
break
end
}

Is there a “simpler” solution?

Best regards,

Jari W.

On Dec 8, 11:48 am, Jari W.
[email protected] wrote:

arr.each_index {|v, i|
Best regards,

Jari W.

I guess it depends on how much efficiency you need. You could always
do:

arr = [5, 3, 1]
insert_value = 2
(arr << insert_value).uniq!
arr.sort!.reverse!

yermej wrote:

insert_value = 2
Is there a “simpler” solution?
(arr << insert_value).uniq!
arr.sort!.reverse!

Thanks for the tip! I optimized it into:

arr = [5, 3, 1]
insert_value = 2
arr.sort! { |a,b| b <=> a } if !(arr << insert_value).uniq!

(Only sorts on no-duplicate and sort in correct direction first time
around.)

Best regards,

Jari W.

On Dec 8, 2007, at 5:03 PM, Mike D. wrote:

array.sort_by won’t work becuase it’s not a hash (although i tried
adding a hash and then sorting by that hash, but that didn’t work
either
(got a nomethod error).

I’m not sure why you are looking for a hash.

data = [
[ “model1”, “instance of 1”, Time.now],
[ “model2”, “instance of 2”, Time.now - 100]
]

p data.sort_by { |x| x[2] } # => [[“model2”, “instance of 2”, Sat
Dec 08 17:15:41 -0500 2007], [“model1”, “instance of 1”, Sat Dec 08
17:17:21 -0500 2007]]

Gary W.

On Dec 8, 2007, at 3:27 PM, Jari W. wrote:

Thanks for the tip! I optimized it into:

arr = [5, 3, 1]
insert_value = 2
arr.sort! { |a,b| b <=> a } if !(arr << insert_value).uniq!

That is a lot of work to insert an item into a sorted array.

Ruby 1.9 has find_index which makes it easy to locate an
insert position. Based on that here are some ideas:

class Array
if RUBY_VERSION < “1.9.0”
def find_index(miss=nil)
each_with_index { |e,i| return i if yield(e) }
miss
end
end

def insert_when(x)
pos = find_index { |e| yield(e, x) }
insert(pos, x) if pos
self
end
end

a = [1,3,5]

a.insert_when(4) {|e, x| e >= x } # [1,3,4,5]
a.insert_when(4) {|e, x| e >= x } # [1,3,4,4,5]

a.insert_when(3) {|e, x|
break if e == x
e > x
} # [1,3,4,4,5]

On Sun, Dec 9, 2007 at 1:48 AM, Jari W.
[email protected] wrote:

arr = [5, 3, 1]
insert_value = 2

arr.each_index {|v, i|
break if v == insert_value
if insert_value > v
arr.insert(i, insert_value)
break
end
}

1 that breaks if value inserted is less than minimum
2 inserting/modifying while walking the array may not be defined for
ruby

Is there a “simpler” solution?

your scheme is clear and simple enough, just fix some edge cases…
then you’ll be ready for your newly special insert method

eg,

?> class Array

def insert2 value
pos=self.size
existing=false
each_with_index do |v, i|
?> if v == value
existing=true
break
elsif value > v
pos=i
break
end
end
insert(pos,value) unless existing
end
end
=> nil

?>
?> arr = [5, 3, 1]
=> [5, 3, 1]

arr.insert2 6
=> [6, 5, 3, 1]
arr.insert2 2
=> [6, 5, 3, 2, 1]
arr.insert2 0
=> [6, 5, 3, 2, 1, 0]
arr.insert2 3
=> nil
arr
=> [6, 5, 3, 2, 1, 0]

best regards -botp

Gary W. wrote:

On Dec 8, 2007, at 3:27 PM, Jari W. wrote:

Thanks for the tip! I optimized it into:

arr = [5, 3, 1]
insert_value = 2
arr.sort! { |a,b| b <=> a } if !(arr << insert_value).uniq!

That is a lot of work to insert an item into a sorted array.

sorting after each insertion is a bad idea. there is a quite simple
binary search that provide super effective way to insert into sorted
arrays. It could be done, for example, like this:

class SortedArray < Array

def self.[] *array
SortedArray.new(array)
end

def initialize array=nil
super( array.sort ) if array
end

def << value
insert index_of_last_LE(value), value
end

alias push <<
alias shift <<

def index_of_last_LE value
# puts “Insertgin #{value} into #{inspect}”
l,r = 0, length-1
while l <= r
m = (r+l) / 2
# puts “#{l}(#{self[l]})–#{m}(#{self[m]})–#{r}(#{self[r]})”
if value < self[m]
r = m - 1
else
l = m + 1
end
end
# puts “Answer: #{l}:(#{self[l]})”
l
end
end

The difference is great if comparison and/or copy is expesive (say,
strings), and when array is long:

bm(12) do |x|
x.report(‘SortedArray’) { a = SortedArray[]; 10000.times { a <<
rand(300) } }
x.report(‘Array.sort’) { a = []; 10000.times { a << rand(300); a.sort!
} }
end

                  user     system      total        real
SortedArray 0.180000 Â Â 0.000000 Â Â 0.180000 ( Â 0.192040)
Array.sort    3.500000   0.010000   3.510000 (  3.515578)

Though, single sort! for a big array is times more effective than adding
elements to SortedArray one by one. Checked on 1.9.1.

2010/5/13 Sergey C. [email protected]:

sorting after each insertion is a bad idea. there is a quite simple
binary search that provide super effective way to insert into sorted
arrays. It could be done, for example, like this:

Or this:

http://raa.ruby-lang.org/project/ruby-bsearch/

Kind regards

robert