pemistahl
pemistahl

Reputation: 9584

How to filter numbers that contain unique digits?

You have a list of numbers and you want to filter out those numbers that contain unique digits, i.e. each digit may only occur once in the number.

Positive examples:

Negative examples:

How would you do that? My own idea is to convert each number to a string and then check whether the size of the set made out of the string's characters is equal to the length of the string. Something like that:

def uniques(numbers):
    for number in numbers:
        str_number = str(number)
        if len(set(str_number)) == len(str_number):
            yield number

for i in uniques(xrange(1000, 1050)):
    print i

1023
1024
1025
1026
1027
1028
1029
1032
1034
1035
1036
1037
1038
1039
1042
1043
1045
1046
1047
1048
1049

Is there a way to do it without converting the integers to strings first?

Upvotes: 9

Views: 2973

Answers (4)

John Dvorak
John Dvorak

Reputation: 27277

If you want a regex-based solution, consider the following regex :

(?![\d.]*([\d.])[\d.]*\1)^[\d.]+$

That is, match a sequence of digits and periods if there are no duplicate digits and there is not a duplicate decimal point.

Update (thanks @frb): the correct way to write this in Python is

re.match(r"(?![\d.]*([\d.])[\d.]*\1)^[\d.]+$",str_number).group(0)

Upvotes: 3

viraptor
viraptor

Reputation: 34145

Using collections.Counter:

from collections import Counter

def unique(seq):
    return any(x > 1 for x in Counter(seq).values())

This will work for any sequence, not only strings.

And only now I noticed that you didn't want to convert to strings... not sure why, but I'll let the answer stay.

Upvotes: 1

unutbu
unutbu

Reputation: 879391

Is there a way to do it without converting the integers to strings first and then convert them back?

Yes, you could use divmod to find the digits base 10, but this is not faster than the method you posted:

def uniques2(numbers):
    for number in numbers:
        seen = set()
        quotient = number
        while quotient > 10:
            quotient, remainder = divmod(quotient, 10)
            if remainder in seen:
                break
            else:
                seen.add(remainder)
        else:
            yield number

Upvotes: 9

Ivan Yurchenko
Ivan Yurchenko

Reputation: 566

Try:

def predicate(n):
    s = repr(n)
    return len(s) == len(set(s))
filtered_numbers = [ n for n in numbers if predicate(n) ]

or, if you prefer filter function:

filtered_numbers = filter(predicate, numbers)

or:

filtered_numbers = filter(lambda n: len(repr(n)) == len(set(repr(n))), numbers)

Upvotes: 5

Related Questions