1

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)
10
  • What does it do? Commented Jun 21, 2017 at 22:50
  • Your code has unnecessary things, for example to use enumerate, besides that you do not explain the purpose of your code. Commented Jun 21, 2017 at 22:51
  • 3
    I'm pretty sure the Pythonic way to solve this problem is check = collections.Counter(sys.argv[1]) and total = len(sys.argv[1]) (if I'm understanding what you're trying to do correctly, which is not necessarily the case). I'd never use map, reduce or filter here (nor in most Pythonic code), so I have no idea why you're asking about them. Commented Jun 21, 2017 at 22:52
  • It's a simple piece of code that assigns a value to a string based on its position in the string. The point isnt the code but how can the process of looping, adding to a dict and cumulative total be done pythonically. Commented Jun 21, 2017 at 22:52
  • 2
    Is enumerate really necessary? The index i is never used. Commented Jun 21, 2017 at 22:58

4 Answers 4

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)
Sign up to request clarification or add additional context in comments.

7 Comments

A slower but easier to understand alternative to n * (n+1) // 2 would be sum(range(n+1)).
@Rawing Is that really slower and not optimised? (I can't run it myself, currently.)
@Rawing yeah, but then you might as well just sum as you count.
The thing here to note is that OP wanted a demo of comprehensions, not an answer that was computationally efficient... anyway.
Oh if OP literally meant map, reduce, and filter, then none of these answers cut it.
|
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

1

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

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.