pritesh_ugrankar has asked for the wisdom of the Perl Monks concerning the following question:

Respected Monks,

Came across this question on LinkeIn for Perl. Someone asked how to find a missing number if in a list of numbers. I came up with following.

use warnings; use strict; my @arr = (19,17,22,23,24,15,16,25..35); sub find_missing { my @stuff = @_; my @sorted_arr = sort {$a <=> $b} @stuff; foreach my $num(0..$#sorted_arr-1) { my $current = $sorted_arr[$num]; my $next = $sorted_arr[++$num]; my @missing = ($current+1..$next-1); print "$current,"; if ($next - $current != 1) { print "(missing: [@missing]),"; } } print "$sorted_arr[-1]"; } &find_missing(@arr);

The output is shown below:

C:\>perl findmissing.pl 15,16,17,(missing: [18]),19,(missing: [20 21]),22,23,24,25,26,27,28,29 +,30,31,32,33,34,35

Is there a better way to write this? I have to do a -1 on the $#sorted_array. I am sure there is a better way, but just dont seem to get it.

On a side node, quite disappointingly, It took me quite a while to write this one. I've started re learning perl and thought I was doing good, till this one came up and shattered my confidence. :) I am glad I figured it out, but it sure took me a long time.

Update:I did several updates to the script so please pardon them.
Thinkpad T430 with Ubuntu 16.04.2 running perl 5.24.1 thanks to plenv!!

Replies are listed 'Best First'.
Re: Better way of writing "find the missing number(s)"
by 1nickt (Canon) on Apr 03, 2017 at 18:31 UTC

    Hi pritesh_ugrankar.

    A hash is usually the best-suited tool for finding out whether elements in one list are in another. Add a key to the hash for each element in the list you are checking, then see if a key exists for each element in the list to be matched.

    use strict; use warnings; use feature 'say'; my @x = ( -2, 2, 3, 8, 10 ); my %y = map { $_ => 1 } @x; say "missing: $_" for grep { not exists $y{ $_ } } ( -2 .. 10 );
    perl 1186862.pl missing: -1 missing: 0 missing: 1 missing: 4 missing: 5 missing: 6 missing: 7 missing: 9

    Hope this helps!


    The way forward always starts with a minimal test.

      Hey Nick,

      I had only a few minutes to play with this when it first came up, but I couldn't accomplish what I wanted with the time I had. What I did have was very similar. That said, you've hard-coded your array when doing the grep(), which probably isn't realistic in production, and it's a bit outside of what OP was after. To do it out of order and without hardcoding, an extra sort needs to be put in place. It could be done inline, but this makes it a bit more clear I think:

      use strict; use warnings; use feature 'say'; my @x = ( -2, 3, 2, 10, 15, 8 ); my %y = map { $_ => 1 } @x; @x = sort {$a <=> $b} @x; say "missing: $_" for grep { ! exists $y{$_} } $x[0] .. $x[-1];

      Hi,

      Wow. This is going to take some time to understand. But I'll take it one line at a time. :) Thank you very much for sharing your method.

      Thinkpad T430 with Ubuntu 16.04.2 running perl 5.24.1 thanks to plenv!!

        You are welcome. Hint: to understand a statement with map and/or grep, read it from right to left.


        The way forward always starts with a minimal test.
Re: Better way of writing "find the missing number(s)"
by tybalt89 (Monsignor) on Apr 03, 2017 at 19:34 UTC
    #!/usr/bin/perl # http://perlmonks.org/?node_id=1186862 use strict; use warnings; my @arr = (19,17,22,23,24,15,16,25..35); my ($low, $high) = (sort {$a <=> $b} @arr)[0, -1]; my @missing = 0 .. $high; @missing[@arr] = ('') x @arr; print "missing numbers are @missing[$low .. $high]\n";
      Bravo! :D //

      This is ingenious in its simplicity :)

Re: Better way of writing "find the missing number(s)"
by Cristoforo (Curate) on Apr 03, 2017 at 18:15 UTC
    Hi pritesh_ugrankar

    Not a better way perhaps, but a solution using Set::IntSpan.

    #!/usr/bin/perl use strict; use warnings; use Set::IntSpan; my @arr = (19,17,22,23,24,15,16,25..35); my $set = Set::IntSpan->new(\@arr); print join ' ', $set->holes->elements;

    Prints:

    18 20 21

      Hi,

      Thanks for the update. I didn't know about that module.

      Thinkpad T430 with Ubuntu 16.04.2 running perl 5.24.1 thanks to plenv!!
Re: Better way of writing "find the missing number(s)"
by tybalt89 (Monsignor) on Apr 03, 2017 at 20:40 UTC

    In the spirit of TIMTOWTDI :)

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1186862 use strict; use warnings; my @arr = (19,17,22,23,24,15,16,25..35); my @sorted = sort {$a <=> $b} @arr; my @missing = map { $sorted[$_ - 1] + 1 .. $sorted[$_] - 1 } 1 .. $#so +rted; print "missing numbers are @missing\n";
      eh! bravo as always tybalt89 but stop beating me on time (as well as in quality..)!

      ;=)

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Better way of writing "find the missing number(s)"
by tybalt89 (Monsignor) on Apr 03, 2017 at 21:26 UTC

    There hasn't been a regex solution yet, so here's one as a one-liner :)

    perl -le '"@{[sort{$a<=>$b}19,17,22,23,24,15,16,25..35]}"=~/\b\d+ (?=( +\d+))(?{push@a,$&+1..$1-1})(*F)/;print"@a"'
Re: Find the missing number
by oiskuu (Hermit) on Apr 04, 2017 at 18:18 UTC

    Numbers 1..n, one missing.

    #! /usr/bin/perl -wl use List::Util qw( sum ); my @arr = (1..3,5..7); print +- sum -1-@arr .. -1,@arr;

      That's a well oiled twist.

      Everyone looking at arrays and comparisons when there was a mathematical solution hiding in plain sight.

        ... a mathematical solution hiding in plain sight.

        Perhaps better to say "hiding in obscurity." From the OP (emphasis added):

        ... find a missing number if in a list of numbers. ...

        ...
        my @arr = (19,17,22,23,24,15,16,25..35);
        ...

        But the list in the code example clearly has more than one missing number. See also the (current) thread title. I think the goose chase, although wild, was justified.


        Give a man a fish:  <%-{-{-{-<

      inspired by your use of sum, the expected sum(1..n) = n*(n+1)/2, so take the found sum from the expected sum:

      use List::Util qw(sum); my @arr=(1..3,5..7); print +(1+@arr)*(2+@arr)/2 - sum(@arr);

      update: clarification = use 1+ and 2+ here because an element is missing from @arr

Re: Better way of writing "find the missing number(s)" -- oneliner
by Discipulus (Canon) on Apr 03, 2017 at 20:48 UTC
    Hello pritesh_ugrankar,

    you got easier answers, pardon me for the following (long)oneliner:

    perl -E "@sr=sort{$a<=>$b}@ar=(5..7,8,10,-3);map{$h{$_}++}$sr[0]..$sr[ +-1],@ar;say for grep{$h{$_}==1}sort{$a<=>$b} keys %h" -2 -1 0 1 2 3 4 9

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Better way of writing "find the missing number(s)"
by salva (Canon) on Apr 04, 2017 at 06:48 UTC
    The question posted in LinkedIn is different:

    Given an increasing sequence of numbers from 1 to n with only one missing number, how can you find that missing number

    The solution to that problem can be found using binary search in O(logN).

      ah! given the above task the oneliner is much simpler!

      perl -E "@ar=(1..3,5,6,7);$h{$_}++for$ar[0]..$ar[-1];delete$h{$_}for@a +r;print keys %h" 4

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        Slices, slices...

        perl -E '\@h{1..(@a=(1..3,5,6,7))+1};delete@h{@a};say%h'
        hey, let's play golf!
        perl -e'$_>++$i&&die"$i\n"for 1..3,5,6,7'

        Or an array version.

        perl -E '@b=0..(@a=(1..3,5,6,7))+1;@b[0,@a]="";say@b'

      Hi Salva,

      Thanks for pointing that out. I missed that. :(

      Thinkpad T430 with Ubuntu 16.04.2 running perl 5.24.1 thanks to plenv!!
Re: Better way of writing "find the missing number(s)"
by pritesh_ugrankar (Monk) on Apr 04, 2017 at 11:22 UTC

    Hi Monks,

    Thank you for your responses. Indeed, Perl is powerful and flexible.

    Thinkpad T430 with Ubuntu 16.04.2 running perl 5.24.1 thanks to plenv!!
Re: Better way of writing "find the missing number(s)"
by tybalt89 (Monsignor) on Apr 04, 2017 at 15:57 UTC

    Using the updated problem (numbers starting at 1 in order, only one missing), here's something completely DIFFerent :)

    #!/usr/bin/perl -l # http://perlmonks.org/?node_id=1186862 use strict; use warnings; use Algorithm::Diff qw(traverse_sequences); my @arr = (1..3,5,6,7); traverse_sequences( \@arr, [ 1 .. @arr + 1 ], { DISCARD_B => sub { print "missing: ", 1 + $_[1] } } );