# Homework time! Find me the lowest even digit in a string

Another brilliant question from Stack Overflow turned into a challenge!

Write a program that:

1. Accepts a string as user input (not piped in or a command line argument). The data type of the input must be a string, if the language does auto evals on input, it must be cast to a string.
2. Finds the index (0 or 1 based) of the first lowest even digit.
3. Prints 2 integers, the index and the lowest even digit.
4. If there is no even digit, return -1 as the index, the second number can be anything.
5. For the purposes of this zero is not even.

Test examples, given the string on the left output the values on the right:

25376  -->  0,2
523726 -->  1,2
583746 -->  4,4
53771  --> -1,0
_4___2 -->  5,2


The winner: is the program with the lowest character count. No bonuses, no penalties.

edit: (about stdin) I screwed up on the user input part, basically no piping data in, and no command line arguments. You must provide a user prompt of some kind within the program itself.

• What if the lowest even integer has two digits? Ok, twas a joke. – Dr. belisarius Dec 20 '13 at 4:49
• What's wrong with stdin? – John Dvorak Dec 20 '13 at 5:26
• @JanDvorak Because arbitrary rules, thats why. – user8777 Dec 20 '13 at 8:24
• Why the limitation of zero not being even? That seems rather odd. – Iszi Dec 20 '13 at 16:56
• Your input requirements really make no sense: For most systems, input comes from stdin, whether prompted by the program or no. Whether or not the input is piped in or not is not under control of the program. -- If your aim was to have users create an interactive program, then you should have been precise about the input and output (including formatting), and the interaction required. – MtnViewMark Dec 21 '13 at 4:11

# Golfscript, 26 (28) characters

'#{gets}'.'246'&8+$1<.@\?  Example: ;'583746' .'246'&8+$1<.@\?
#44

;'53771'
.'246'&8+$1<.@\? #8-1  Explanation: • '#{gets}' is an escape to ruby to satisfy the I/O requirements; STDIN is assumed to be empty • . clones the input • '246'&8+ finds which even digits are present in the input, except 8 is always retained • $1< sorts the list, then takes the first digit, but keeps it as a string
• .@\? orders the stack {digit, input, digit}, then finds the digit in the input; thanks @peterTaylor for noticing me of the [array, array] signature of ?.

I've been very bold with the spec interpretation this time; namely:

• The spec does not mandate in which order the integers are output; Use the other @peter's suggestion :x?x to fix (I don't like temporary variables.
• The spec does not require any delimiter between the two integers; since the digit found is always a single digit, this does not matter much. If it does, append n@ (which also performs the swap), n\ (which doesn't), or ] (which formats the output as ["8" -1]).
• They aren't crazy requirements, they are arbitrary requirements :P – user8777 Dec 20 '13 at 8:26
• Also, the spec requires the index to be first, but I can't read GolfScript well enough to tell if you didn't – user8777 Dec 20 '13 at 8:28
• @LegoStormtroopr the only point of the spec that mentions the order is #4: "If there is no even digit, return -1 as the index, the second number can be anything". #3 does not specify the order; what about the "no delimiter" boldness? – John Dvorak Dec 20 '13 at 8:30
• Oh snap, you are correct sir! I mentioned the two numbers to output, but not the order. I stand corrected! – user8777 Dec 20 '13 at 8:32
• You can replace .0=@? with .@\? or :x?x to save one character and avoid the doubt over the order. Nice use of 8 as a fallback. – Peter Taylor Dec 20 '13 at 9:38

## APL (37)

(1-based by default, but obeys ⎕IO)

{×⍴m←⍵/⍨⍵∊'2468':z,⍨⍵⍳⍕z←⌊/⍎¨m⋄¯1,0}⍞


Explanation:

• ⍞: read user input
• m←⍵/⍨⍵∊'2468': remove all the characters that aren't 2468, store in m.
• ×⍴m: see if there are any
• :: If so:
• z←⌊/⍎¨m: evaluate each character in m, find the lowest number and store in z.
• z,⍨⍵⍳⍕z: return the index of z in ⍵, followed by z.
• ⋄: If not:
• ¯1,0: return (-1, 0)
• Wow, I've beat APL (not with the J solution, but still...) – John Dvorak Dec 23 '13 at 13:50
• Explanation? APL is so self-explaining... ;) – german_guy Apr 17 '14 at 18:52

Python 3 ,69

combining ideas from here.

s=input();print(([(s.find(x),x)for x in'2468'if x in s]+[(-1,0)])[0])

• I like the use of a string rather than a list, and the resulting omission of the (now removed)  quotes. – SimonT Dec 24 '13 at 15:45
• @boothby I guess you didn't notice i'm using python 3. In python 3 input() Accepts a string as user input. – Wasi Dec 27 '13 at 8:08
• @SimonT  (backticks) are not just quotes. It is a deprecated alias for repr() in python 2. But its no longer available in Python 3. So, If I wanna use  on something in python 3, i have to write repr(something) which cost 4 more chars. Thats why i get rid of it ;) – Wasi Dec 27 '13 at 8:30
• @Wasi yes indeed, my mistake. – boothby Dec 27 '13 at 21:06
• @Wasi, yes thank you. I was aware of that and by "now removed" I actually meant removed as a part of the language altogether. Cheers. – SimonT Dec 28 '13 at 2:06

# Ruby, 60 characters

x=gets.chars.sort.find{|c|c=~/[2468]/};p x ?$_.index(x):-1,x  • $_ contains the last input read by gets.
• p calls inspect on the arguments and prints the results.

## perl - -94- 53 chars (or 48)

Instead of index()'s zero-based approach we use pos and start at one; no CPAN. Run this with perl -nE and it will wait for input (which it can also receive from STDIN).

The first match (//) operation works on the default value ($_) from input, matching against the given even numbers, sorting the matches into an array, and then storing them in a "scalar list" ($m). In scalar context the list value of the sorted array is one item long and thus consists of the lowest matched even digit in the number.

Having isolated the lowest even matched digit, we then use $m for a second match operation which will find the first occurrence of the value of $m in the original string. We are still using the default value here $_ and we are saving the match against the contents of $m in $1 (the reason for the () around $m in the second statement). After that, pos and say do the rest (and in fewer characters than index and print).

perl -nE'($m)=sort/[2468]/g;/($m)/g;$1?say pos.",$1":say"-1,0"'
33467
3,4


If output order or commas don't matter then it's possible to make this 5 chars shorter:

($m)=sort/[2468]/g;/($m)/g;$1?say$1.pos:say"-10"


In my earlier attempt I rushed and made an error - I just wanted to beat python and ruby (but failed) ... sigh.

Congrats to the winner and the other participants on their cool solutions - especially to perl6 :-) A possible Perl6 adaptation of this approach could use ".comb (suggested on freenode by Masak et.al.).

$_=get;my$n=.comb(/<[2468]>/).min;/$n/;say$/.from//-1,",$/"  • Was so focused on beating python/ruby that I posted a one liner that did not work when no even numbers were present: eagerly using an earlier version of the above from my $SHELL history for the "win". This working version is ridiculously long and embarrassing. Serves me right: will shorten later to avoid shame :-( – G. Cito Apr 17 '14 at 2:08
• older hack: if(/[2468]/){for$i(2,4,6,8){if(index($_,$i)>-1){say$i,",",index($_,$i)+1;last}}}else{say"0,-1"} – G. Cito Apr 17 '14 at 17:45
• Good, but room is left for further golfing: ($m)=sort/[2468]/g;/$m/g;say$&?pos.",$&":"-1,0" (47+1 bytes, AFAIK -n costs an additional byte). – Heiko Oberdiek Apr 17 '14 at 19:00
• NB. pos returns the offset. So in effect it's returning 1-based index in your example. – draegtun Apr 17 '14 at 19:13
• @draegtun oopsie :-) thanks. @Heiko true that: switches should count otherwise perl's -M would win every time ;-) – G. Cito Apr 17 '14 at 19:22

# Perl 6, 375560 characters

$_=get;say m/<{~.comb(/<[2468]>/).min}>/.from//-1,',',$/.Int


EDIT: I misunderstood the question the first time. This update should be correct. It finds the lowest number of 2,4,6, or 8 that the input contains, and then does a regex match against the input with that lowest number. $/.from gets the position of the match and ',',$/.Int is a comma plus the match itself, or 0 if the text didn't match.

While you're here, go upvote G. Cito, who helped me fix my entry in the comments and on irc :)

• Unfortunately this prints first even number (2468) found and not the lowest :( NB. Also there is an issue with $/.from when nothing is found (this maybe a bug in my old version of Rakudo). Here's a variation of your solution which works (for me!) my$x=get;for 2,4,6,8 ->$n{if$x~~/$n/ {say$/.from,",$/";exit}};say "-1 0"; Comes in at 75 chars but I'm sure a shorter perl6 solution can be made. – draegtun Apr 17 '14 at 13:35 • @draegtun ... I made a similar error with my perl5 attempt yesterday (see below). My fix added 70 chars! Just updated it with a ~50 char version - which seems to work. I think if you sort all the even digits in the number you can then do a match using the (now) first digit in the sort and be sure you have the lowest and the first. Am I right? – G. Cito Apr 17 '14 at 18:23 • @G.Cito Looks good to me and have already +1 your answer. I can see way to shave off a few chars but I see Heiko as done even better than what I was going to suggest! – draegtun Apr 17 '14 at 19:04 • @draegtun Oh, geez, I completely misunderstood the question then :( Thank you so much! I'll update this ASAP – Mouq Apr 17 '14 at 22:23 • @mouq Here's an approach one that uses the same approach as the perl5 example I posted below: my$n; $_="333992";$n=.comb(/<[2468]>/).min;/$n/;say$/.from//-1,",$/". I learned about .comb about 20 minutes ago and I already like it :-) – G. Cito Apr 17 '14 at 22:30 # J, 44 characters echo(;{&i){._1,~(#~(#i)&>)'2468'i.~i=.1!:1]1  outputs the last character of the input if no even digit is found Example:  echo(;{&i){._1,~(#~(#i)&>)'2468'i.~i=.1!:1]1 523726 +-+-+ |1|2| +-+-+ echo(;{&i){._1,~(#~(#i)&>)'2468'i.~i=.1!:1]1 53771 +--+-+ |_1|1| +--+-+  Explanation: • echo ... i=.1!:1]1 is input/output. Way longer than I want it to. Input is stashed away into i as well. • '2468'i.~ finds the first occurence of each even digit in the input, returning the input length if the digit cannot be found • (#~(#i)&>) reads "select by the length of input being greater than this"; in other words, select those indices that point into the input. Way longer than I want it to. • _1,~ appends -1 to the back. Underscore is the J's way of representing negative values. • {. selects the first element • (;{&i) concatenates it with the input character at that position in two boxes; since we're concatenating a number with a character, non-boxing concatenation (,) won't do. If unboxed display is desired, the index needs to be formatted (":) first at a cost of two chars. # Python 2.7 - 76 A reference answer I fully expect to be beaten. But to start people off: s=raw_input() print[(s.index(e),e)for e in[2,4,6,8]ifein s][:1]or(-1,0)  • you can shave off a character by changing index to find; you can also change [2,4,6,8] to 2,4,6,8 (with space in front). – Justin Dec 20 '13 at 6:37 • Shorter: print[(s.find(e),e)for e in 2,4,6,8ifein s+'8'][0] – grc Dec 20 '13 at 7:18 • I think you broke your own rule... Since when is raw_input not stdin? – Cruncher Dec 20 '13 at 15:40 • print[(s.index(c),c)for c in'2468'if c in s][0]or(-1,0) – Eric Dec 20 '13 at 22:06 ## R, 104 y=as.integer(strsplit(scan(,""),"")[[1]]);z=which(!y%%2&y);a=z[which.min(y[z])];cat(a,"-1 0"[!sum(a)],y[a])  Examples (in R, the index starts at 1): _4_502 6 2 _3_501 -1 0 583746 5 4  ## Powershell, 7673 71 $s=read-host;2,4,6,8|%{if($i=$s.indexof("$_")+1){"$i,$_";break}};"-1,0"  • $s=read-host stores user input into $s • 2,4,6,8|%{...} runs what's inside {} for digits 2,4,6,and 8 • $i=$s.indexof("$_")+1 sets $i to 1-based index of a digit in $s or 0 if the digit is not found
• if( $i=... ){...} the condition will be true when $i is not 0
• "$i,$_";break outputs the index and the digit, and stops execution
• "-1,0" otherwise outputs -1,0
• This does not output the proper index. In PowerShell, as in the test cases given for the question, indexes start at zero. – Iszi Dec 21 '13 at 9:28
• Rule #2 says the index can be 0 or 1 based. – Danko Durbić Dec 21 '13 at 9:32
• Oh. Didn't spot that. Either way, here's a zero-based variant that's only 5 characters longer. $s=read-host;(2,4,6,8)|%{if(($i=$s.indexof("$_"))-gt-1){"$i,$_";break}};'-1,0' – Iszi Dec 21 '13 at 9:34
• Also, I'm pretty sure the parenthesis around 2,4,6,8 are unnecessary. – Iszi Dec 21 '13 at 9:35
• Zero-based variant can be made 2 characters longer, if you change the if condition to: if(1+($i=$s.indexof("$_"))) – Danko Durbić Dec 21 '13 at 9:38 # C++11, 173 (including two new-line chars) Here's the compressed version: #include<iostream> #include<string> int main(){std::string s;std::cin>>s;for(char c:"2468")for(char&k:s)if(k==c){std::cout<<(&k-&s[0])<<','<<c;return 0;}std::cout<<"-1,8";}  Here's the readable version: #include<iostream> #include<string> int main() { std::string s; // auto s = ""s; (since C++14: -3 characters) std::cin >> s; for (char c : "2468") for (char& k : s) if (k == c) { std::cout << (&k - &s[0]) << ',' << c; return 0; } std::cout << "-1,8"; }  Here's an older version: C++11, 175 (including two new-line chars) Here's the compressed version: #include<iostream> #include<string> int main(){std::string s;std::cin>>s;for(char c:"2468"){int i=s.find(c);if(i<s.size()){std::cout<<i<<','<<c;return 0;}}std::cout<<"-1,8";}  Here's the readable version: #include<iostream> #include<string> int main() { std::string s; std::cin >> s; for (char c : "2468") { int i = s.find(c); if (i < s.size()) { std::cout << i << ',' << c; return 0; } } std::cout << "-1,8"; }  • Try #define o std::cout<<. I think it trims your code by 1 character. – user10766 Dec 28 '13 at 16:15 • @user2509848, nope, it's actually 2 characters longer. But thanks anyway :) – Shoe Dec 28 '13 at 16:18 • k==c?std::cout<<(...),throw 0:0; - the conditional operator can replace if, when all you need are expressions. throw is an expression and also one character shorter than return. – MSalters Jan 3 '14 at 14:04 ## C, 80 character main(){char a[81],b=strcspn((gets(a),a),"2468");printf("%d,%c",a[b]?b:-1,a[b]);}  ## C# - 124 I'm a bit late to the party void Main(string[]a){var x=a[0].Where(w=>char.IsDigit(w));var y=x.Min();Console.Write(y%2==0?a[0].IndexOf(y)+","+y:"-1,0");}  ## Haskell, 79 characters s&d=filter((==d).snd)$zip[-1..]$'0':s main=interact$show.head.("24680">>=).(&)


This runs a little fast and loose with the output formatting:

& echo 523726 | runhaskell 16083-LowEven.hs
(1,'2')
& echo 583746 | runhaskell 16083-LowEven.hs
(4,'4')
& echo 53771 | runhaskell 16083-LowEven.hs
(-1,'0')
& echo _4___2 | runhaskell 16083-LowEven.hs
(5,'2')
& echo 25376 | runhaskell 16083-LowEven.hs
(0,'2')


# PowerShell: 145 103

Changes from original
Removed error quelch per comment from asker.

Code:

"$([array]::IndexOf(($s=(read-host)-split''|?{$_}),($x=$s|?{$_%2-eq0}|?{$_-gt0}|sort|select -f 1))),$x"


Walkthrough:

"..." Everything within the quotes will be included in the output.

$(...) Everything between the parenthesis will be treated as a commands, and the output of that code will be included instead of the commands themselves. [array]::IndexOf(...) This is used to get the index of an element with the array. Its first parameter is the array object to be searched. The second parameter is the object to be found. This only outputs the index of the first matching element. I tried simply using an IndexOf method directly against $s (e.g.: $s.IndexOf($x), but this fails for some reason I've yet to figure out - it claims the method doesn't exist on that object.

(...) Expression within the parenthesis will be the first parameter for IndexOf.

$s=$s will be a variable to store the user input.

(read-host)-split'' Gets user input, and splits it into an array of characters.

|?{$_} Filters out extra empty array elements that are generated by -split''. ,(...) Expression within the parenthesis will be the second parameter for IndexOf. $x= $s will be a variable to store the lowest even digit from the user input. $s|?{$_%2-eq0} Pulls out the even numbers from$s.

|?{$_-gt0} Invalidates zero. |sort Sorts the remaining objects, default order is ascending. |select -f 1 Picks the first object, which will by now be the lowest even digit. ,$x The above code will output the index of our lowest even digit. This adds a comma, and then outputs the digit itself.

Notes:

This is perhaps stretching rule #4 a little bit. If there is no even digit, the output will not include a second number.

Also, this will throw some non-terminating errors if the input includes non-numeric elements. The script will still run and give proper output, but you might want to set $ErrorActionPreference to 'SilentlyContinue' before running it. • @DankoDurbić Now that you've soundly defeated me, would you mind helping me figure out why I couldn't use $s.IndexOf here? It was giving me an error saying the method didn't exist for that object, even though $s|gm said it did. It works on the system I'm on now, but not the one I was trying to do it on earlier today. Is it a version compatibility issue? Earlier system was PSv2, current is PSv4. – Iszi Dec 21 '13 at 10:13 • Your $s is string[], and $s.indexof doesn't work because ...[System.String[]] doesn't contain a method named 'indexOf', which seems reasonable (I'm running PS 2.0). $s|gm returns members of System.String, not System.String[]. I'm not sure why it does that. – Danko Durbić Dec 21 '13 at 11:02

vb.net (108c)

Sub Main()
For Each c In "2468"                  '20c
i=t.IndexOf(c)                        '14c
If i>=0 Then Exit For                 '21c
Next                                  ' 4c
WriteLine("{0},{1}",i,If(i<0,0,t(i))) '37c
End Sub


Smells slightly cos it's exploiting VB's dynamic setting. Option Explicit Off and Imports System.Console.

VB.net (193c) using LINQ

This one is primarily utilising a LINQ query.

Dim r = From d In "2468" Group Join q In ReadLine.Select(Function(c, p) New With {.c = c, .i = p})
On d Equals q.c Into g = Group
Skip While g.Count = 0 ' 138c
WriteLine(If(r.Any,r(0).g(0).i&","&r(0).g(0).c,"-1,0"))        '  55c


## Golf-Basic 84, 91 characters

:L1/L1_L2iL1:fpart(L1,2)2_L1:SortA(L1)@cumSum(L1)=length(L1):Thend-1#dcumSum(L1):EnddL1

• Make sure there's no 0 in the input (it's not even, who said it had to be odd?)
• Get input into L1 (List 1)
• Loop finding modulus of 2 for each value
• Sort L1 from smallest to highest
• Display the index (either -1 or the cumulative sum) and the smallest number

## Bourne shell (sh), 88 chars

(echo 88;sed 's/./\n&/g')|nl -v-1|awk '{print $2,$1}'|sed '/^[^2468]/d'|sort -n|head -n1


I thought I'd do a string-processing thing for fun. Technically it reads from stdin, but if you run it interactively it'll prompt the user for input since the keyboard is the default stdin stream. Uses basic shell commands to split up the input on lines, number them (using the ill-known nl utility), filter out lines fitting the criteria, sort them and output the lowest-digit one. Prints 88 -1 if no even digit was found.

# C++ - 223 221 Characters:

#include<iostream>
#include<string>using namespace std;
int main(){int s=9;int p=-1;string x;cout<<"Enter characters: ";cin>>x;for(int i=0;i<x.length();i++){int n=x[i]-48;if(n%2==0&n<s){s=n;p=i;}}cout<<p<<", "<<s;return 0;}


#include<iostream>
#include<string>
using namespace std;

int main() {
int s = 9;
int p= - 1;
string x;

cout << "Enter characters: ";
cin >> x;

for (int i = 0; i < x.length(); i++) {
int n = x[i] - 48;

if (n % 2 == 0 & n < s) {
s = n;
p = i;
}
}

cout << p << ", " << s;

return 0;
}


It is long, but not as bad as Java.

• It's actually 221 + 2 (new line characters) otherwise it won't compile. – Shoe Dec 28 '13 at 15:55
• Oh. Word only detected 221, I will change it. – user10766 Dec 28 '13 at 16:08

# Javascript 93 characters

b=prompt();((r=b.match(/[2468]/g)||[-1]).sort(function(a,b){return a-b})[0])+","+b.indexOf(r)


Get all event numbers, sort the array and prints the first digit.

# Mathematica, 157 characters

f = Row@{If[(x = Min@#) == Infinity, -1, Position[#, x][[1, 1]]],
",", x} &[IntegerDigits@ToExpression@StringReplace[#,
{"0" -> "1", x_?DigitQ :> x, _ -> "1"}] /. _?OddQ -> Infinity] &

inputs = {"25376", "523726", "583746", "53771", "_4___2"}

f /@ inputs // Column


1,2

2,2

5,4

-1,∞

6,2

# Julia, 84

julia> f(a)=(for i=2:2:8;(k=search(a,'0'+i))==0||return(k,i);end;(-1,0))
583746
(5,4)
_4___2
(6,2)


Julia really needs a shorter way to get input for golfing. I counted the definition of f and one call to f. One based indexing is why the answers are different from the examples.

# JavaScript - 53 Characters

for(y=prompt(x=0);x<8&&(i=y.indexOf(x+=2))<0;);x+''+i


Reads input from prompt and outputs (to the console) the lowest even character then (without a delimiter) the first index of that character; if there are no non-zero even characters then it outputs 8-1.

# C, 110 Characters

i,f,r,c,m=9;main(){while(r=getchar()>31){c=r-48;i++;if(c>=0&&~c&1&&c<m)m=c,f=i;}printf("%i,%i",m>8?-1:f-1,m);}


ungolfed:

i,f,r,c,m=9;
main(){
while(r=getchar()>31){
c=r-48;
i++;
if(c>=0&&~c&1&&c<m) m=c,f=i;
}
printf("%i,%i",m>8?-1:f-1,m);
}


# Javascript, 69

a=prompt(),b=[-1,9];for(i in a)if(+a[i]%2==0&&+a[i]<b[1])b=[+i,+a[i]]


Reads the value, iterates through each character, if the number representation mod 2 equals 0 and the number is less that what is already stored in b, replace the values in b

# Rebol, 69

s: input for n 2 8 2[if f: find s n[print[index? f f/1]q]]print[-1 0]


# Ruby 55

p gets.chars.sort*''=~/[2468]/?[$_.index($&),$&]:[-1,0]  ## JavaScript/jQuery 149 Golfed $(function(){
var i=prompt(),k=-1,n=0,c,e="2468";
while(n<i.length){c=i.charAt(n);k=e.indexOf(c);if(k>=0){break;}n++;}$('#d').append(k+","+n); });  Full source <!DOCTYPE html> <html> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> </head> <body> <div id="d"></div> <script type="text/javascript">$(function(){
var i=prompt();
var k=-1;
var n=0;
var c;
var e="2468";
while(n<i.length){
c=i.charAt(n);
k=e.indexOf(c);
if(k>=0){
break;
}
n++;
}
\$('#d').append(k+","+n);
});
</script>
</body>
</html>