I wrote a Counter
subclass suitable for anagrams, with a more visually user-friendly __str__()
method using alphabetic ordering and Unicode superscripts(/subscripts) for letter-counts, so you can compare at-a-glance if two phrases are anagrams or not:
c1 = LetterCounter("PRESIDENTTRUMP")
print(c1)
D¹ E² I¹ M¹ N¹ P² R² S¹ T² U¹
Code below. My questions:
- translating integer to superscript(/subscript) is really clunky. Anything more compact?
Counter.items()
actually returns a view. It would be more elegant if it was similarly possible to definesorted_items()
as a view instead of takingsorted(list(self.items()))
- leave the subscript code in even though it's not currently used
- applying
.format()
to a variable-length list of tuples is a pain, can't just use *args.'{0[0]}{0[1]}'.format(x for x in value_count)
fails with'generator' object is not subscriptable
'{0[0]}{0[1]} '.format([x for x in value_count])
is wrong, the format is outside the list comprehension.[ '{0[0]}{0[1]} '.format(x) for x in value_count ]
won't allow us callLetterCounter.int_to_superscript()
on only the second item of each tuple.- any more elegant approaches?
- in general we're supposed to override
__str__()
not__repr__()
, but if we don't care about pickling why not just override__repr__()
, then we can directly see the object just by typing its name, don't need print? - after I independently wrote this I found Printing subscript in python
- it seems more friendly for general use/reposting to enter Unicode literals in source as '\u2070' rather than '⁰'
Code:
import collections
class LetterCounter(collections.Counter):
subscript_digits = ''.join([chr(0x2080 + d) for d in range(10)])
superscript_digits = ''.join(['\u2070','\u00b9','\u00b2','\u00b3','\u2074','\u2075','\u2076','\u2077','\u2078','\u2079'])
trans_to_superscript = str.maketrans("0123456789", superscript_digits)
trans_to_subscript = str.maketrans("0123456789", subscript_digits)
@classmethod
def int_to_superscript(cls, n):
return str(n).translate(cls.trans_to_superscript)
@classmethod
def int_to_subscript(cls, n):
return str(n).translate(cls.trans_to_subscript)
def __str__(self):
value_count = sorted(list(self.items()))
return ' '.join( '{}{}'.format(x[0], LetterCounter.int_to_superscript(x[1])) for x in value_count )
# Test code
c1, c2 = LetterCounter("PRESIDENTTRUMP") , LetterCounter("MRPUTINSREDPET")
print(c1)
D¹ E² I¹ M¹ N¹ P² R² S¹ T² U¹