6
\$\begingroup\$

Output all numbers in certain range containing certain digits

Task

  1. Take an input of digits through console, prompt, or a file (in the decimal number system). These digits should be separated by spaces.
  2. Take an input of the "maximum" through console, prompt, or a file
  3. If you use a file, the program must output "[Data taken from file]"
  4. Print all natural numbers (including 0) below the maximum containing at least one of the digits specified by the user (they may contain other digits), separated by a single space. Leading 0s do not count!
  5. If there is a sequence of 10 or more consecutive numbers, it should output the first and last, separated by a -->

Rules

  • Standard loopholes are (obviously) not allowed
  • Programs must complete the tasks above
  • The answer with the fewest characters wins

Examples

>>> 1 3
>>> 100
1 3 10 --> 19 21 23 30 --> 39 41 43 51 53 61 63 71 73 81 83 91 93

>>> 1 2 4 5 6 7
>>> 29
1 2 4 5 6 7 10 --> 28

>>> 0 1 2 3 4 5 6 7 8 9
>>> 9999999
0 --> 9999998

[Data taken from file]
1 3 5 7 9
\$\endgroup\$
3
  • \$\begingroup\$ Please be specific about the format for digits input: space separated (as in examples), comma separated, a single string (136) or whatever. \$\endgroup\$
    – edc65
    Commented Sep 21, 2014 at 15:09
  • \$\begingroup\$ Does the arrow have to be -> or would a Unicode arrow like also be fine? \$\endgroup\$ Commented Sep 21, 2014 at 20:21
  • \$\begingroup\$ @MartinBüttner It has to be -->. \$\endgroup\$
    – laurencevs
    Commented Sep 21, 2014 at 20:30

13 Answers 13

5
\$\begingroup\$

Ruby, 186 181 177 bytes

a=gets.split;puts ([0]+(0...gets.to_i).select{|x|(x.to_s.split('')&a).any?}).each_cons(2).slice_before{|m,n|m+1<n}.map{|a|c=a.map &:last;c.count>9?[c[0],'-->',c.last]:c}.join' '

Ungolfed explanation:

# Read numbers from input and split into array
a = gets.split

# Loop from 0 to maximum number, exclusive
puts ([0]+(0...gets.to_i).select { |x| 
  # Only include numbers that include at least one input digit
  (x.to_s.split('')&a).any?
})    # Add dummy element to the beginning of array
.each_cons(2)   # Get each array of 2 consecutive elements
.slice_before { |m, n| #
  m + 1 < n # "Slice" the array if m and n are not consecutive
}.map { |a|
  c = a.map &:last # Get all the last elements in `a`
  if c.count > 9
    # 10 or more consecutive elements. Get the first and last, and
    # an arrow in-between
    [c[0],'-->',c.last]
  else
    c
  end
}.join ' ' # Add space in-between all elements, and print result
\$\endgroup\$
4
\$\begingroup\$

JavaScript (E6) 153 157 161 163 177

Input and output via prompt

The main loop filters the numbers with a regexp and builds spans of consecutive numbers (as arrays). The O function concatenates the spans converting them to string. If the elements in a span array are 10 or more, instead of taking all elelements it just take the first and last number.

Test in FireFox/FireBug console

P=prompt,m=P(d=P());
O=_=>(
  o+=s[9]?s[0]+" -->"+p:s.join(''),s=[]
);
for(s=[o=''],p=n=0;n<m;++n)
  (n+'').match("["+d+"]")&&
  (
    n+~p&&O(),s.push(p=' '+n)
  );
O(),P(o)
\$\endgroup\$
3
\$\begingroup\$

GolfScript 74 71 (DEMO)

' ':§/)~\n*:x;,{`x&},99:c;{.c)=!{§\}*:c}%[§]/{.,9>{('-->'@)\;}{~}if}%§*

Description:

The code expects the parameters passed via the console, like so:

echo "1 3 100" | ruby golfscript.rb numbers.gs

Here's the annotated code:

' ':§/                  # Split input by space and save the 
                        # space character in variable `§`

)~                      # Take the last input and convert to int
                        # This is the "maximum" parameter.

\n*:x;                  # Take the list of digits and join them with 
                        # newlines, resulting in a string.
                        # Save the result in variable `x` and take it
                        # off the stack

,                       # Take the "maximum" parameter and make an 
                        # array [0, max)

{`x&},                  # Filter the array, leaving only numbers that
                        # contain the right digits (that have at least
                        # one char in common with string `x`)

99:c;                   # Save the value 99 in a new variable, `c`.

{                       # For each element (`n`) in the array:
  .                     #   - duplicate `n` on the stack
  c)=!{§\}*             #   - if it does not equal `c`+1 add a space before it
  :c                    #   - assign the value of `n` to `c`
}%

[§]/                    # Split the resulting array by spaces

{                       # For each element `a` in the array of arrays:
  .                     #   - duplicate `a` on the stack
  ,9>                   #   - check if length > 9
  {('-->'@)\;}          #   - if `a`'s length is >9, then push "(min)", "-->" and "(max)"
                        #     on the stack
  {~}if                 #   - otherwise, just place the elements of the current array 
                        #     on the stack
}%
§*                      # make a string representing the elements of the resulting array,
                        # separated by spaces
\$\endgroup\$
3
\$\begingroup\$

Mathematica, 233 191 171 165 bytes

t=StringSplit;Flatten[Split[Cases[Range@Input[d=t@InputString[]]-1,m_/;ToString@m~t~""⋂d!={}],#2-#<2&]/.{e:{__Integer}/;Length@e>9:>{#&@@e,"-->",Last@e}}]~Row~"  "

I'm taking the input from two prompts which is as close to command line as it gets in Mathematica.

Ungolfed:

t = StringSplit;
Flatten[
  Split[
    Cases[
     Range@Input[d = t@InputString[]] - 1,
     m_ /; ToString@m~t~"" \[Intersection] d != {}
     ],
    #2 - # < 2 &
    ] /. {e : {__Integer} /; Length@e > 9 :> {# & @@ e, "-->", Last@e}}
  ]~Row~"  "

I'm simply filtering a range with all numbers to the relevant ones and then I'm collapsing consecutive subsequences with a repeated rule replacement.

\$\endgroup\$
9
  • \$\begingroup\$ Shorter, using String to aid finding runs: t = StringSplit; Row[Flatten[ Split[ (Cases[Range[Input[d = t@InputString[]] - 1], m_ /; ToString@m~t~"" \[Intersection] d != {}]), #2 - # == 1 &] /. {e : {a__Integer} /; Length[{a}] > 9 :> {e[[1]] -> e[[-1]]}}], " "] \$\endgroup\$
    – DavidC
    Commented Sep 21, 2014 at 19:53
  • \$\begingroup\$ @DavidCarraher Nice! Thanks a lot. I don't think the -> shortcut is fair game though. I'll ask the OP. \$\endgroup\$ Commented Sep 21, 2014 at 20:13
  • \$\begingroup\$ Shouldn't it be Range[0, Input[d = t@InputString[]] - 1] instead of Range[Input[d = t@InputString[]] - 1]? \$\endgroup\$
    – DavidC
    Commented Sep 21, 2014 at 23:04
  • \$\begingroup\$ @DavidCarraher Oh, I didn't see that 0 was included. But in that case decrementing the default range by 1 is shorter. \$\endgroup\$ Commented Sep 21, 2014 at 23:05
  • \$\begingroup\$ Yes, it's shorter, but it only tests through max-1, not max. \$\endgroup\$
    – DavidC
    Commented Sep 21, 2014 at 23:40
3
\$\begingroup\$

Ruby, 105 (or 110 if you need to add .chop to the gets to make your input work)

Run with command line flags pl.

r=/[#$_]/
$_=(?1...gets).grep(r).slice_before{|e|-1>eval("#@l-"+@l=e)}.map{|a|a[9]?[a[0],'-->',a[-1]]:a}*' '

Convert the first input to a character class in a regex. Generate all numeric strings between 1 and the next input (exclusive) and find the ones that match the regex. slice_before trick is stolen from August to find contiguous ranges, it gets shorter by using an instance variable to remember the last element so I don't need each_cons, but longer since I'm working with strings. I check for ranges of size 10 or more by seeing whether they have a 10th element or not (a[9]), and replace their internal elements with the arrow.

\$\endgroup\$
3
\$\begingroup\$

Perl - 170 162

$a=<>;chop$a;$a=~s/ /|/g;@b=grep{/$a/}(0..<>-1);while(++$c<@b){if($b[$c]-$b[$c-1]>1){($e=$c-$d)>9&&splice(@b,$d+1,$e-2,'-->')&&($c-=$e-3);$d=$c}}print join' ',@b

Ungolfed with comments:

$a=<>;    #Read digits
chop$a;$a=~s/ /|/g;    #Turn digit string into regex
@b=grep{/$a/}(0..<>-1);    #Assign @b as list of numbers from 0 to second input which match the digits
while(++$c<@b){    #Loop $c through indexes of list
    if($b[$c]-$b[$c-1]>1){    #If next number is not consecutive, do arrow adding stuff
        ($e=$c-$d)>9&&    #If the number of previous consecutive numbers is at least 10
            splice(@b,$d+1,$e-2,'-->')&&    #Replace all but the first and last with '-->'
            ($c-=$e-3);    #Subtract from index to compensate for decreased list length
        $d=$c    #Set $d (fist index of consecutive range) to current index
    }
}
print join' ',@b    #Print the list
\$\endgroup\$
2
\$\begingroup\$

Python - 208 202 211

D=raw_input()
n=input()
s=[`i`for i in range(n)if sum(d in`i`for d in D)]
i=0
while i<len(s):
 j=len(s)
 while j:
  j-=1
  try:
   if int(s[j])-int(s[i])==j-i>8:s[i+1:j]=['-->']
  except:1
 i+=1
print' '.join(s)
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Doesn't this produce a wrong output formating? For me (Python 2.7) it outputs something like ['1', '2', '3']. \$\endgroup\$
    – Emil
    Commented Sep 23, 2014 at 17:16
  • \$\begingroup\$ @Emil: Sorry, fixed it. \$\endgroup\$
    – Falko
    Commented Sep 23, 2014 at 20:58
2
\$\begingroup\$

Bash+coreutils, 94 bytes

seq -s\  0 $[$1-1]|sed -r "s/\b[^ ${2// }]+\b/:/g
s/([0-9]+)( [0-9]+){9,}/\1 -->\2/g
s/: ?//g"
  • seq generates all numbers below the maximum
  • The first sed line replaces any number not containing the interesting digits with :
  • The second sed line searches for runs of 10 or more space-separated numbers and replaces with start --> end
  • The final sed line removes unnecessary : and spaces.

Output:

$ ./belowdigits.sh 100 "1 3"
1 3 10 --> 19 21 23 30 --> 39 41 43 51 53 61 63 71 73 81 83 91 93 
$ ./belowdigits.sh 29 "1 2 4 5 6 7"
1 2 4 5 6 7 10 --> 28
$ ./belowdigits.sh 999999 "0 1 2 3 4 5 6 7 8 9"
0 --> 999998
$ 
\$\endgroup\$
1
\$\begingroup\$

Python, 227

Currently the longest, but whatever.

a=raw_input().split()
l=[i for i in range(input())if any(x in str(i)for x in a)]
for i in l:
 for j in l[::-1]:
  try:L=l.index;l=l[:L(i)+1]+['-->']+l[L(j):]if j-i==L(j)-L(i)and j-i>8 else l
  except:1
print' '.join(map(str,l))
\$\endgroup\$
1
\$\begingroup\$

Haskell, 195 194

main=interact$(\[d,m]->v$f(any(`elem`f(/=' ')d).s)[0..read m-1]).lines
s=show
f=filter
v l@(x:m)|g l>x+8=unwords[s x,"->",s$g l,v$f(>g l)l]|0<1=s x++' ':v m
v[]=""
g s=f(`notElem`s)[s!!0..]!!0-1

not very short but Imho quite good for this specific question and language.

\$\endgroup\$
1
\$\begingroup\$

Python 2, 202 191

Another try at Python, using the same idea as @August's Ruby solution:

a=raw_input()
s=[x for x in range(input())if any(c in`x`for c in a)]
for _,g in __import__('itertools').groupby(s,lambda x:x+1in s):
 l=map(str,g)
 print len(l)<9and' '.join(l)or l[0]+' -->',

PS: Coincidentally, the first two lines ended up nearly identical to both other Python entries.

\$\endgroup\$
1
\$\begingroup\$

R 224 w/o Comment

a=as.character
l=length
x=a(scan())                                                 # Read digits
s=a(0:(scan()-1))                                           # Read max number
p=as.numeric(s[Reduce(`|`,lapply(x,function(x)grepl(x,s)))])# Find all numbers we want
j=1                                                         # Print the arrow "-->"
for (i in 1:l(z<-c(rle(diff(p))$l,1))){if(z[i]<9)cat(p[j:(j+z[i]-1)],"")else
cat(p[j],"-->","")
j=j+z[i]}
\$\endgroup\$
1
\$\begingroup\$

PHP, 273 258 255 248 256 239 234 bytes

What a mess.

Submission:

function n($s,$b){$r=$q=$f="";for($g=-1;$g++<$b;){$i=0;foreach(explode(" ",$s)as$d){$i+=in_array($d,str_split($g));}if($i&&$b-$g){$q[]=$g;$f=$f?:$g;}elseif($f){$r.=($g-$f>9?$q[0]." --> ".($g-1):implode(" ",$q))." ";$q=$f="";}}echo$r;}

Exploded view:

function n($s, $b) {
  $r = $q = $f = "";
  for ($g = -1; $g++ < $b;) {
    $i = 0;
    foreach(explode(" ", $s) as $d) {
      $i += in_array($d, str_split($g));
    }
    if ($i && $b-$g) { 
      $q[] = $g;
      $f = $f ?: $g;
    } elseif ($f){ 
      $r .= ($g-$f > 9 ? $q[0] . " --> " . ($g-1)
                       : implode(" ", $q)        ) . " ";
      $q = $f = "";
    }
  }
  echo $r;
}

Variable explanations:

$b: string, upper bound (input)
$s: string, digit string (input)
$d: int, individual digit in $s
$g: int, current guess
$i: int, number of times a digit exists in the current guess
$f: int, first correct guess in a chain
$q: array, correct guess queue
$r: string, final result (output)

This is super unoptimized, still tweaking it.

Edits:
-18: Combining variable inits, merging two $i inits.
-7: Removing $a declaration.
+8: Fixed two bugs: now allows 0 as output, now allows $b-1 and --> $b-1 as output.
-17: God, I love ternary operators.
-5: Let's treat $i as an int full-time, and simplify the resets.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.