good chemistry is complicated,and a little bit messy -LW PerlMonks

### Closest-value-in-list Golf!

by larryk (Friar)
 on May 29, 2001 at 17:47 UTC Need Help??

Inspired by neophyte's question in the chatter about parsing a list of numerical data for the nearest value to a given (number) value, I tee off timidly and prepare for an ass-whoopin'.

Rules
1. sub must take 2 params:
1) list (or listref) of _numerical_ data (minimum 1 element)
2) a number - int/real/negative - whatever! as the "match-closest-to-this" number
not necessarily in that order.
2. the original order of the data must be maintained - you can't sort the data directly.
3. can't think of any more!

Update: 19:45 29/05/2001 (GMT)
Apologies if I did not make this clear: 2 things go into the sub - a number and a list. the order in which you pass these is up to you. the list can either go in as a strtaight list or as a reference to a list - your choice - just so long as the original isn't modified. and the return value from the sub should be either the value closest to the given number or the index in the original list.

e.g.

```sub g {blah}

print "Closest value is ",g(@list,\$number);
# or
print "Closest value is ",g(\$number,@list);
# or
print "Closest value is ",g(\@list,\$number);
# or
print "Closest value is ",g(\$number,\@list);
# OR
print "Closest value is ",\$list[g(@list,\$number)];
# or
print "Closest value is ",\$list[g(\$number,@list)];
# or
print "Closest value is ",\$list[g(\@list,\$number)];
# or
print "Closest value is ",\$list[g(\$number,\@list)];

```#!perl -w
use strict;

chomp(my @list = <DATA>);

# FORE!!! 78 chars.
sub g {my%h=();my\$n=pop;\$h{abs(\$_-\$n)}=\$_ for@_;my@s=sort{\$a<=>\$b}keys
+%h;\$h{shift@s};}

print g(@list,10);

__DATA__
17
1.4
18.2
7.9
12.2
12.5
1.1
7.8
18.3
20
6.7
6.9
18.1
1.5
17.7
16.6
1.2
1.3
17.5
I can't believe I had to leave a space in!

"Argument is futile - you will be ignorralated!"

Replies are listed 'Best First'.
Re (tilly) 1: Closest-value-in-list Golf!
by tilly (Archbishop) on May 29, 2001 at 18:40 UTC
First of all I don't think that strict is a very useful restriction in golf. For instance a useful trick is to use punctuation variables like \$, because Perl parses \$,for differently than \$_for. But using \$, is strict-compliant in letter even though it definitely is not in spirit!

That said the natural approach at 46 letters happens to be strict-compliant:

```sub g{
(sort{abs(\$a-\$_[1])<=>abs\$b-\$_[1]}@{\$_[0]})[0]
}
how come you use ( ) on one abs but not the other? :-)
```sub g {(sort{abs\$a-\$_[1]<=>abs\$b-\$_[1]}@{\$_[0]})[0]} # 44 chars
Update:
pop and no ref...
```sub g {\$n=pop;(sort{abs\$a-\$n<=>abs\$b-\$n}@_)[0]} # 39 chars
fantastic - that's exactly half my original

"Argument is futile - you will be ignorralated!"

For future reference, I found your spec unclear. In the end I settled on "2 parameters" as being the clearest statement. But it would help in the future if you gave sample test code using the function. Then there can be no doubt.

However if I am allowed to rearrange the arguments, I find it more natural that any special arguments should come first in the list. Giving a 38 character solution:

```sub g {
(sort{abs\$a-\$_[0]<=>abs\$b-\$_[0]}@_)[1]
}
Update my bad... sorry, missed the or listref bit of golf, my response here was useless
- Ant
Re: Closest-value-in-list Golf!
by chipmunk (Parson) on May 29, 2001 at 20:26 UTC
I disagree with tilly that using sort() is the natural solution. I think this is the natural solution:
```sub closest {
\$n=pop;\$c=pop;abs\$n-\$_<abs\$n-\$c?\$c=\$_:0for@_;\$c
}
This is O(n) rather than O(n*log n). Of course, at 47 characters it is, unfortunately, a longer solution. Oh well!
Re: Closest-value-in-list Golf!
by suaveant (Parson) on May 29, 2001 at 17:57 UTC
Why are you using strict in golf if it is not required?
```sub g {\$n=pop;\$h{abs(\$_-\$n)}=\$_ for@_;@s=sort{\$a<=>\$b}keys%h;\$h{shift@
+s};}
66 chars with strict off

Update

```sub g {\$n=pop;\$h{abs(\$_-\$n)}=\$_ for@_;@s=sort{\$a<=>\$b}keys%h;\$h{shift@
+s}}
oops trailing ; is extraneous... 65 strokes...

Update2 oooh!

```sub g {\$n=pop;\$h{abs(\$_-\$n)}=\$_ for@_;\$h{(sort{\$a<=>\$b}keys%h)[0]}}
59 strokes
- Ant
```sort{\$a<=>\$b}keys%h
is too much, since \$a<=>\$b is the default sort. that leaves you with
```sub g {\$n=pop;\$h{abs(\$_-\$n)}=\$_ for@_;\$h{(sort keys%h)[0]}}
for 52.
did you test this? i think you'll find that \$a cmp \$b is the default.

"Argument is futile - you will be ignorralated!"

that does not work... I tried it already... returns 20
- Ant

Re: Closest-value-in-list Golf!
by jeroenes (Priest) on May 29, 2001 at 19:12 UTC
I can offer a 47 36 char solution with the use of PDL:
```sub i{my\$a=pdl\$_[0];minimum_ind(abs(\$a-\$_[1]))}
and it is strict-compliant and very straightforward.

Of course it returns the index, but that is allowed, isn't it? Otherwise, add 4 chars to get the value.

Hey wait, do you only count the chars between {}? Than we can take of 7, that counts 40 chars. And there is room in the last part:

```sub i{my\$a=pdl\$_[0];minimum_ind abs\$a-pop}
And we are at 36 chars.

Jeroen
"We are not alone"(FZ)

Why do you not count "use PDL"?
Should have mentioned that. Add 7 chars.

Use of modules actually should disqualify a golf, methinks. It's just that I've come to like matrix calc, and couldn't resist such a solution.

(boo) Re: Closest-value-in-list Golf!
by boo_radley (Parson) on May 29, 2001 at 17:58 UTC
oops :
Warning: Use of "keys" without parens is ambiguous at C:\trash.pl line 8.
my answer is forthcoming, will update.
update you can shave off a few chars by changing
```\$h{shift@s}
to
```\$h{\$s[0]}
. Don't get too complex, eh? UpdateTime's up!
```sub e {sub n{abs(\$s-\$b)};\$s=pop;while(\$b=pop){if(&n<\$d||!\$d){\$l=\$b;\$d=
+&n}}\$l}
for a total of 70 chars.
even shorter to not assign to @s at all, like my Update2
- Ant
sub g {\$n=pop;\$h{abs\$_-\$n}=\$_ for@_;\$h{(sort{\$a<=>\$b}keys%h)[0]}}

57 if you take out the ( ) on the abs

"Argument is futile - you will be ignorralated!"

Re: Closest-value-in-list Golf!
by Masem (Monsignor) on May 29, 2001 at 18:41 UTC
Here's a 62 char solution, imploying half a schwatzian transform:
```sub g{
#234567890123456789012345678901234567890123456789012345678901234567890
my\$c=pop;(sort{@\$a[1]<=>@\$b[1]}map{[\$_,abs(\$_-\$c)]}@_)[0]->[0]
}

Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: Closest-value-in-list Golf!
by ChOas (Curate) on May 29, 2001 at 18:13 UTC
Hey!,

I think it`s shorter, and I got no spaces...

But I don`t know how to count...

```sub g{%_=map{abs(\$_[-1]-\$_[\$_]),\$_}(0..@_-2);\$_[\$_{(sort{\$a<=>\$b}keys%
+_)[0]}]}

GreetZ!,
ChOas

print "profeth still\n" if /bird|devil/;
count between the {}s of the sub - yours is 71

"Argument is futile - you will be ignorralated!"

Re: Closest-value-in-list Golf!
by ChOas (Curate) on May 29, 2001 at 18:46 UTC
55 ... and now I give up....

```sub g{%_=map{abs\$_[-1]-\$_,\$_}@_;\$_{(sort{\$a<=>\$b}keys%_)[1]}}

GreetZ!,
ChOas

print "profeth still\n" if /bird|devil/;
Re: Closest-value-in-list Golf!
by mr_mischief (Monsignor) on May 31, 2001 at 06:55 UTC
Not the shortest, but this was what was obvious to me...
```
sub c {
#_123456789_123456789_123456789_123456789_12345
\$i=pop;for(@_){\$c=\$_ if abs\$i-\$c>abs\$i-\$_}\$c
}

Chris
Re: Closest-value-in-list Golf!
by mr.nick (Chaplain) on May 30, 2001 at 22:47 UTC
Hm. I used a different approach, and if my eyes don't deceive me, it's the lowest score yet. Update: No it's not ... damn:
```sub g {
#          1         2         3         4         5
# 1234567890123456789012345678901234567890123456789012
\$n=pop;\$,=pop;\$,=abs\$n-\$_<abs\$n-\$,?\$_:\$,for@_;\$,
}
for a score of 48.

Create A New User
Node Status?
node history
Node Type: perlmeditation [id://83923]
Approved by root
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2018-05-26 04:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
World peace can best be achieved by:

Results (192 votes). Check out past polls.

Notices?