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
    Sep 21, 2014 at 15:09
  • \$\begingroup\$ Does the arrow have to be -> or would a Unicode arrow like also be fine? \$\endgroup\$ Sep 21, 2014 at 20:21
  • \$\begingroup\$ @MartinBüttner It has to be -->. \$\endgroup\$
    – laurencevs
    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
    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\$ 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
    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\$ Sep 21, 2014 at 23:05
  • \$\begingroup\$ Yes, it's shorter, but it only tests through max-1, not max. \$\endgroup\$
    – DavidC
    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
    Sep 23, 2014 at 17:16
  • \$\begingroup\$ @Emil: Sorry, fixed it. \$\endgroup\$
    – Falko
    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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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