Find the closest items in an array to a given value

Hi!

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

It's for a little music game, where player has to tap at the right moment,
notes are register in a array, with the correct time, and as players can't
be accurate as a computer, i need some error margin. I working with 0.05
rounded values.

Also, if someone knows a good way to synchro Scrolling and Music with tempo
value that would be great...
I mean, not the conversion to find how many frames between each beats. but
how to convert, the number of frames between 2 beat to a distance in
pixel...? ( and possibly with a set-able speed) ?

Thanks.

···

--
View this message in context: http://www.nabble.com/find-the-closest-items-in-an-array-to-a-given-value.-tf4714369.html#a13475984
Sent from the ruby-talk mailing list archive at Nabble.com.

It's crucial to know whether your sequence of values is always sorted. If it is, you can use #each_cons and return the pair where left is less-or-equal than x and right is greater-than x.

If not, you can just iterate through the array and remember the last match and its distance - overwrite when smaller distance.

  robert

PS: A nice task for #inject. :slight_smile:

···

On 29.10.2007 21:19, trebor777 wrote:

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

Something like this? (Supposing the array may not be in order)

def array_between(a=, v=0.0)
  return if a.empty?
  return [v, a.min] if v < a.min
  return [a.max, v] if v > a.max
  ret =
  ret.push a.find {|num| num < v}
  ret.push a.find {|num| num > v}
  ret.push v if ret.size < 2
  return ret.sort
end

Just a quick 10-second sketch... Maybe you should have tried this too.
And i know you from other forums too! Nice to see you here trebor!!

···

2007/10/29, trebor777 <mrobert@trebor777.net>:

Hi!

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular
step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

It's for a little music game, where player has to tap at the right moment,
notes are register in a array, with the correct time, and as players can't
be accurate as a computer, i need some error margin. I working with 0.05
rounded values.

Also, if someone knows a good way to synchro Scrolling and Music with
tempo
value that would be great...
I mean, not the conversion to find how many frames between each beats. but
how to convert, the number of frames between 2 beat to a distance in
pixel...? ( and possibly with a set-able speed) ?

Thanks.

--
View this message in context:
http://www.nabble.com/find-the-closest-items-in-an-array-to-a-given-value.-tf4714369.html#a13475984
Sent from the ruby-talk mailing list archive at Nabble.com.

trebor777 wrote:

Hi!

I'm searching for a light method for finding the closest items to a
given
value.

[1,2,3,4,5,6].min { |a,b| (a-value).abs <=> (b-value).abs }

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

install rbtree - it has upper_bound and lower_bound methods that do just this - otherwise you can do something like this:

cfp:~ > cat a.rb
require 'alib'

Array.send :include, alib.bsearch

list = %w( a b c c c d e f )
index = list.bsearch_first {|x| x <=> "c"}
p list[index - 1 .. index + 1]

list = %w( a b c c c d e f )
index = list.bsearch_last {|x| x <=> "c"}
p list[index - 1 .. index + 1]

cfp:~ > ruby a.rb
["b", "c", "c"]
["c", "c", "d"]

a @ http://codeforpeople.com/

···

On Oct 29, 2007, at 2:19 PM, trebor777 wrote:

Hi!

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

It's for a little music game, where player has to tap at the right moment,
notes are register in a array, with the correct time, and as players can't
be accurate as a computer, i need some error margin. I working with 0.05
rounded values.

--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama

trebor777 wrote:

Hi!

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

Of course there are many ways. Here's one. Add your target value to
the array and sort, then return the values before and after it in
sequence.

Note that this code 'wraps' the values in the array -- a target value
lower than the lowest value in the array, or higher than the highest
value, will return the starting and ending values.

#!/usr/bin/ruby -w

# a is array of values, x is target value
def nearest(a,x)
    tmp = a.sort
    tmp << x
    tmp.sort!
    i = tmp.index x
    [tmp[i-1], tmp[i+1]]
end

a = [ 1, 1.25, 2, 3, 3.5 ]
x = ARGV.shift.to_i

a,b = nearest(a,x)
puts "%0.2f => %0.2f,%0.2f" % [x, a, b]

Hi --

···

On Tue, 30 Oct 2007, Robert Klemme wrote:

PS: A nice task for #inject. :slight_smile:

Is that your .sig? :slight_smile:

David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
   * Advancing With Rails, Edison, NJ, November 6-9
   * Advancing With Rails, Berlin, Germany, November 19-22
   * Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!

Stefan Rusterholz wrote:

trebor777 wrote:

Hi!

I'm searching for a light method for finding the closest items to a
given
value.

[1,2,3,4,5,6].min { |a,b| (a-value).abs <=> (b-value).abs }

Regards
Stefan

Since you want more than 1 you can either implement your own min method
that returns the 2 smallest ones, that'd happen in O(n), or you can sort
by the distance, which is O(nlogn):
ary.sort_by { |item| (value-item).abs }.first(n)
If you just want all values that are a max difference of x from y, you
can use:
ary.select { |item| item - x <= y }

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

It's crucial to know whether your sequence of values is
always sorted.

arr = [...]
sorted_arr = arr.sort

PS: A nice task for #inject. :slight_smile:

Unfortunately, from tests I have done in other threads, it appears that
inject is not very efficient, i.e. it's slow.

Flávio Lisbôa wrote:

def array_between(a=, v=0.0)
  return if a.empty?
  return [v, a.min] if v < a.min
  return [a.max, v] if v > a.max
  ret =
  ret.push a.find {|num| num < v}
  ret.push a.find {|num| num > v}
  ret.push v if ret.size < 2
  return ret.sort
end

Just a quick 10-second sketch... Maybe you should have tried this too.
And i know you from other forums too! Nice to see you here trebor!!

That doesn't work:

a = [1.1, 1.3, 1.4, 2, 2.2, 3]
target = 1.85
p array_between(a, target)

--output:--
[1.1, 2]

detect returns the first value for which block isn't false, which will
be the first value in the array that is less than the given number. The
op wants the biggest number in that array that is less than the given
number, which could be the last number in the array.

[1,2,3,4,5,6].min { |a,b| (a-value).abs <=> (b-value).abs }

That doesn't work. It returns only the closest single value.

ary.sort_by { |item| (value-item).abs }.first(n)

That doesn't work either. It returns the two closes values, which means
that both returned values could be less than or greater than the given
number. The op wants the two closest number that is lower than the
given number and the closest number that is higher than the given
number.

The following are two solutions that appear to do what the op wants.
The first one stores the closest lower value and the closest higher
value in two variables as it steps through the array:

a = [ 1.25, 1, 3, 1.9, 1.95, 2.1, 2.2, 1.5]
target = 1.85

closest_lower = nil
closest_higher = nil

a.each do |elmt|
  if elmt < target
    if !closest_lower or elmt > closest_lower
      closest_lower = elmt
    end

  elsif elmt > target
    if !closest_higher or elmt < closest_higher
      closest_higher = elmt
    end

  else #if elmt == target
    #do something
  end
end

Surprisingly, this is slightly faster--even for very large arrays:

target = 1.85

less_than =
greater_than =

a.each do |elmt|
  if elmt < target
    less_than << elmt

  elsif elmt > target
    greater_than << elmt
  end
end

puts less_than.max
puts greater_than.min

···

--
Posted via http://www.ruby-forum.com/\.

Ara.T.Howard-2 wrote:

Hi!

I'm searching for a light method for finding the closest items to a
given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular
step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

It's for a little music game, where player has to tap at the right
moment,
notes are register in a array, with the correct time, and as
players can't
be accurate as a computer, i need some error margin. I working
with 0.05
rounded values.

install rbtree - it has upper_bound and lower_bound methods that do
just this - otherwise you can do something like this:

cfp:~ > cat a.rb
require 'alib'

Array.send :include, alib.bsearch

list = %w( a b c c c d e f )
index = list.bsearch_first {|x| x <=> "c"}
p list[index - 1 .. index + 1]

list = %w( a b c c c d e f )
index = list.bsearch_last {|x| x <=> "c"}
p list[index - 1 .. index + 1]

cfp:~ > ruby a.rb
["b", "c", "c"]
["c", "c", "d"]

a @ http://codeforpeople.com/
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama

While waiting for solutions, i did something really similar.

a = myhash.keys.sort[value-0.05..value+0.05]

then i just use

myhash[a]

···

On Oct 29, 2007, at 2:19 PM, trebor777 wrote:

--
View this message in context: http://www.nabble.com/find-the-closest-items-in-an-array-to-a-given-value.-tf4714369.html#a13481318
Sent from the ruby-talk mailing list archive at Nabble.com.

Flávio Lisbôa wrote:

Hi!

I'm searching for a light method for finding the closest items to a given
value.

example/

1..10
in a range
i give, 5.5, return 5 and 6 for example....

as i'm working with arrays, containing numbers, but with no regular
step...
it will change i think.
example..

a = [ 1, 1.25, 2, 3, 3.5 ]

if i give 1.85, it gives me 1.25 and 2

It's for a little music game, where player has to tap at the right
moment,
notes are register in a array, with the correct time, and as players
can't
be accurate as a computer, i need some error margin. I working with 0.05
rounded values.

Also, if someone knows a good way to synchro Scrolling and Music with
tempo
value that would be great...
I mean, not the conversion to find how many frames between each beats.
but
how to convert, the number of frames between 2 beat to a distance in
pixel...? ( and possibly with a set-able speed) ?

Thanks.

--
View this message in context:
http://www.nabble.com/find-the-closest-items-in-an-array-to-a-given-value.-tf4714369.html#a13475984
Sent from the ruby-talk mailing list archive at Nabble.com.

Something like this? (Supposing the array may not be in order)

def array_between(a=, v=0.0)
  return if a.empty?
  return [v, a.min] if v < a.min
  return [a.max, v] if v > a.max
  ret =
  ret.push a.find {|num| num < v}
  ret.push a.find {|num| num > v}
  ret.push v if ret.size < 2
  return ret.sort
end

Just a quick 10-second sketch... Maybe you should have tried this too.
And i know you from other forums too! Nice to see you here trebor!!

Well must be RMXP.org.. isn't it?
(although it's been a while i've been there)
I use the same username everywhere i go on the net... xD

···

2007/10/29, trebor777 <mrobert@trebor777.net>:

--
View this message in context: http://www.nabble.com/find-the-closest-items-in-an-array-to-a-given-value.-tf4714369.html#a13481371
Sent from the ruby-talk mailing list archive at Nabble.com.

# a = myhash.keys.sort[value-0.05..value+0.05]
# then i just use
# myhash[a[x]]

can you test without hash?

~> a
=> [1, 1.25, 1.9, 2, 3, 3.5]
~> val
=> 1.85
~> a[val-0.5..val+0.5]
=> [1.25, 1.9]
~> a.slice (val-0.5..val+0.5)
=> [1.25, 1.9]

kind regards -botp

···

From: trebor777 [mailto:mrobert@trebor777.net]

On Behalf Of 7stud --:
# a = [ 1.25, 1, 3, 1.9, 1.95, 2.1, 2.2, 1.5]
# target = 1.85
# closest_lower = nil
# closest_higher = nil
# a.each do |elmt|
# if elmt < target
# if !closest_lower or elmt > closest_lower
# closest_lower = elmt
# end
# elsif elmt > target
# if !closest_higher or elmt < closest_higher
# closest_higher = elmt
# end
# else #if elmt == target
# #do something
# end
# end

···

#
# Surprisingly, this is slightly faster--even for very large arrays:
# target = 1.85
# less_than = []
# greater_than = []
# a.each do |elmt|
# if elmt < target
# less_than << elmt
#
# elsif elmt > target
# greater_than << elmt
# end
# end
# puts less_than.max
# puts greater_than.min

cool and straightforward. works in unordered list.

have you tried #partition?

~> a
=> [1.25, 1, 3, 1.9, 1.95, 2.1, 2.2, 1.5]
~> [(parts=a.partition{|i| i<=1.85}).first.max,parts.last.min]
=> [1.5, 1.9]

in 1.9, there is already group_by, so
~> [(parts=a.group_by{|e| e<=1.85}.values).last.max,parts.first.min]
=> [1.5, 1.9]

..but we may be working too hard if lists is ordered though. and for ordered lists, a binary search like what ara posted should be the fastest.

kind regards -botp

Not until now. Thanks for the inspiration!

Cheers

robert

···

2007/10/29, David A. Black <dblack@rubypal.com>:

Hi --

On Tue, 30 Oct 2007, Robert Klemme wrote:

> PS: A nice task for #inject. :slight_smile:

Is that your .sig? :slight_smile:

--
use.inject do |as, often| as.you_can - without end

Peña, Botp wrote:

have you tried #partition?

~> a
=> [1.25, 1, 3, 1.9, 1.95, 2.1, 2.2, 1.5]
~> [(parts=a.partition{|i| i<=1.85}).first.max,parts.last.min]
=> [1.5, 1.9]

No, I haven't. Thanks for pointing the partition method out to me.

I must have been testing my first method on a different, longer array
because now it runs slightly faster than my second method, as well as
the partition solution, which makes more sense to me. Here are the
results I get:

test1 exec time(1,000,000 loops): 10.348911 total
test2 exec time(1,000,000 loops): 13.916226 total
test4 exec time(1,000,000 loops): 12.912649 total

where test4 is this code:

a = [ 1.25, 1, 3, 2, 3.5, 4.25, 0.65, 1.5, 7.7, 9.2]
target = 1.85

arr = a.partition do |elmt|
  elmt < target
end

closest_lower = arr[0].max
closest_higher = arr[1].min
end

But they are all pretty much equivalent. inject() is so slow, I just
hit ctrl-c after what seems like 5 minutes.

···

--
Posted via http://www.ruby-forum.com/\.

ignore that post.
what was i thinking, .. again? :))

kind regards -botp

# -----Original Message-----
# From: Peña, Botp [mailto:botp@delmonte-phil.com]
# Sent: Tuesday, October 30, 2007 11:19 AM
# To: ruby-talk ML
# Subject: Re: find the closest items in an array to a given value.

···

#
# From: trebor777 [mailto:mrobert@trebor777.net]
# # a = myhash.keys.sort[value-0.05..value+0.05]
# # then i just use
# # myhash[a[x]]
#
# can you test without hash?
#
# ~> a
# => [1, 1.25, 1.9, 2, 3, 3.5]
# ~> val
# => 1.85
# ~> a[val-0.5..val+0.5]
# => [1.25, 1.9]
# ~> a.slice (val-0.5..val+0.5)
# => [1.25, 1.9]
#
# kind regards -botp
#
#