I want to write this code in a more pythonic way using map, reduce, filter. Can someone help me with that.
This is a simple piece of code which assigns a total value to a string based on it's position in the string.
For example, for the string abaacab
a b a a c a b
1 2 3 4 occurrence of a
1 2 occurrence of b
1 occurrence of c
1+たす1+たす2+たす3+たす1+たす4+たす2 =わ 14
import sys
check, total = {}, 0
for i, v in enumerate(sys.argv[1]):
if v in check:
check[v] += 1
else:
check[v] = 1
total += check[v]
print(total)
4 Answers 4
If you want to calculate the result in the way you're currently doing it, I think your current code is about as Pythonic as it can be (the only exception is the unnecessary enumerate call).
However, I think there's a better way to find your total than adding up the counts as you make them. The part of the total contributed by each value can be calculated directly from the final count of that value (it's a triangular number). This means you can count the values in one go and figure out the total at the end.
Here's how I'd do it:
import sys
import collections
counts = collections.Counter(sys.argv[1])
total = sum(n * (n+1) // 2 for n in counts.values())
print(total)
7 Comments
n * (n+1) // 2 would be sum(range(n+1)).import sys
val = {v : sys.argv[1].count(v) for v in set(sys.argv[1])}
total = sum(val[k] * (val[k] + 1) // 2 for k in val)
3 lines... 1 import and 2 lines of vanilla python.
The first line creates a mapping of chars to counts. Then, the second line finds the sum of sequence for each character inside a list comprehension, with a second sum to total the sums of each character.
Output:
val: {'a': 4, 'c': 1, 'b': 2}
total: 14
Caveat: This is not efficient, since it calculates counts for each character (linear complexity).
Comments
SO willing, here's another answer that actually does what you want as you want it. Python3 compliant, haven't tested on Python2.
from functools import reduce, partial
total = reduce(lambda x, y: x + y, map(lambda x: x * (x + 1) // 2, map(partial(str.count, sys.argv[1]), set(sys.argv[1]))))
print(total)
Output
14
Breaking it down:
partial(str.count, sys.argv[1]) ---- (1) defines a higher order function which the map will apply to sys.argv. This way, your counts are created. A dict is not needed for the final answer so it is not created.
map(---(1)---, set(sys.argv[1])) ---- (2) applies the partial function (1)
map(lambda x: x * (x + 1) // 2, ----(2)----) ----(3) takes the counts generated by (2) and then applies AP to get the sum.
reduce(lambda x, y: x + y, ----(3)----) tops it off with a summation of the sums from (3).
Comments
The only way I would say your code could be made more Pythonic is by using a Counter object instead of a regular dict, which streamlines the actual counting being performed by the dict:
>>> from collections import Counter
>>> counts = Counter()
>>> s = "abaacab"
>>> total = 0
>>> for c in s:
... counts[c] += 1
... total += counts[c]
...
>>> total
14
check = collections.Counter(sys.argv[1])andtotal = len(sys.argv[1])(if I'm understanding what you're trying to do correctly, which is not necessarily the case). I'd never usemap,reduceorfilterhere (nor in most Pythonic code), so I have no idea why you're asking about them.enumeratereally necessary? The indexiis never used.