Saikat
Saikat

Reputation: 16750

How to count the number of trailing zeroes in an integer using Java 8 Stream/Lambda?

How to count the number of trailing zeroes in an integer using Java 8 Stream/Lambda?

Basically the logic should be: keep the integer dividing by 10 as long as the remainder is 0 (the quotient will be supplied to the next division) and count the occurrence(s).

e.g.

12300 % 10 == 0 true

1230 % 10 == 0 true

123 % 10 == 0 false

Answer: 2

Note: I prefer not to involve String here :-)

Upvotes: 2

Views: 498

Answers (4)

WJS
WJS

Reputation: 40034

You can do it by just using the three argument IntStream.iterate.

  • v%10 isolates the last digit of v using the remainder operator
  • v/10 effectively removes last digit from v

Using the value under test as the seed.

  • if the last digit is 0, put it on the stream
  • update the value to make the last digit accessible
  • repeat previous two steps until digit is non-zero
int[] testCases = {10222200,229999,22,1000000000,1010,-20000,-222};
for (int tc : testCases) {
     long trailingZeroes = IntStream.iterate(tc, v->v%10==0, v->v/10)
                                    .count();
     System.out.printf("%10d : %2d%n", tc, trailingZeroes);
}

prints

  10222200 :  2
    229999 :  0
        22 :  0
1000000000 :  9
      1010 :  1
    -20000 :  4
      -222 :  0

Upvotes: 1

Kartik
Kartik

Reputation: 7917

Here's another way:-

private static int countTrailingZeroes(int n) {
    int length = n == 0 ? 1 : (int) (Math.log10(n) + 1); //number of digits in n
    return IntStream.rangeClosed(0, length)
            .map(i -> length - i)           //reverse stream
            .map(o -> (int) Math.pow(10, o))
            .filter(o -> n % o == 0)
            .boxed()
            .findFirst()
            .map(i -> (int) Math.log10(i))  //number of digits minus 1
            .orElse(0);
}

@Holger's edit:

private static int countTrailingZeroes(int n) {
    int length = n == 0 ? 1 : (int) (Math.log10(n) + 1); //number of digits in n
    return IntStream.rangeClosed(0, length)
            .map(i -> length - i)           //reverse stream
            .filter(o -> n % (int) Math.pow(10, o) == 0)
            .findFirst()
            .orElse(0);
}

Upvotes: 1

ETO
ETO

Reputation: 7279

Considering you don't have Java9's takeWhile method, this can also do the trick:

static final int[] POWERS_OF_10 = { 1000000000, 100000000, 10000000,
        1000000, 100000, 10000, 1000, 100, 10 };

static int trailingZeros(int number) {
    return Arrays.stream(POWERS_OF_10)      // reversed stream of of 10^n
                 .map(pow -> number % pow)  // stream of rests
                 .reduce(0, (count, rest) -> (rest == 0) ? count + 1 : 0);
}

Upvotes: 1

SergGr
SergGr

Reputation: 23788

If this is a purely hypothetical question, here is a purely hypothetical answer of how you can do it:

static int countZeroes(int value) {
    if(value == 0) // we need to handle this case explicitly
        return 1; 
    IntStream s = IntStream.iterate(value, v -> v / 10);
    return (int) takeWhile(s, v -> v > 0 && v % 10 == 0)
            .count();

}

It uses a helper function takeWhile that is available in Java 9 but not in Java 8 so has to be emulated like this:

// In Java 9 there is a standard takeWhile
// https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#takeWhile-java.util.function.Predicate-
// but in Java 8 I have to emulate it
static IntStream takeWhile(IntStream s, final IntPredicate pr) {
    final Spliterator.OfInt origSp = s.spliterator();

    Spliterator.OfInt filtered = new Spliterators.AbstractIntSpliterator(origSp.estimateSize(), 0) {
        boolean lastPredicate = true;

        @Override
        public boolean tryAdvance(final IntConsumer action) {
            if (!lastPredicate)
                return false;

            origSp.tryAdvance((int v) -> {
                lastPredicate = pr.test(v);
                if (lastPredicate) {
                    action.accept(v);
                }
            });
            return lastPredicate;
        }
    };

    return StreamSupport.intStream(filtered, false);
}

The idea is that

IntStream.iterate(value, v1 -> v1 / 10).takeWhile(v -> v > 0)

should generate a stream of cutting digits at the end one by one and then you can apply takeWhile(v -> v % 10 == 0).count() to count the number of zeros and finally you can merge those two takeWhiles into one.

Upvotes: 3

Related Questions