Reputation: 1231
In Python, is there any counter
available during the list comprehension as it would be in case of a for
loop?
It would be more clear why I need a counter, with this example:
I wish to achieve the following:
Initial List: ['p', 'q', 'r', 's']
Desired List: [(1, 'P'), (2, 'Q'), (3, 'R'), (4, 'S')]
In the desired list, first element of every tuple are ordinal numbers. If it were just flat list, I could have used zip
to achieve this. But however, the list I am dealing with is nested, three level deep (think of hierarchical data), and it is generated through list comprehension.
So, I was wondering is there any way to introduce those ordinal numbers during list comprehension. If not, what would be the best possible solution.
P.S. : Here the lower case letters are converted to uppercase, but that is not a part of problem, think of it as just a data conversion.
Code:
allObj = Category.objects.all()
tree =[(_, l1.name, [(__, l2.name, [(___, l3.name) for l3 in allObj if l3.parentid == l2.categoryid]) for l2 in allObj if l2.parentid == l1.categoryid]) for l1 in allObj if l1.parentid == None]
allObj
contains data from table category, which in turn contains hierarchical data represented in the form of Adjacency List.
I have put _
where I need ordinal numbers to be. Notice that the list is nested, so there will be a separate counter at each level represented by 1, 2 & 3 _
s.
Upvotes: 29
Views: 55619
Reputation: 25
Great thread! The approved answer and later expansions gave me an idea, which I think might help someone. So here it is.
[print('{}. Key: {}, Value: {}'.format(i, k, v)) for i, (k, v) in enumerate(some_list.items(), 1)]
The above can be used to get a pretty output of a dictionary. You can use it for debugging, logging, etc... Oh yes, one more possibility, creating a csv file, so your manager can open it in Excel. Managers love their spreadsheets.
Example output:
1. Key: AwesomeKey1, Value: AwesomeValue1
2. Key: AwesomeKey2, Value: AwesomeValue2
3. Key: AwesomeKey3, Value: AwesomeValue3
4. Key: AwesomeKey4, Value: AwesomeValue4
5. Key: AwesomeKey5, Value: AwesomeValue5
Upvotes: 0
Reputation: 469
I was looking to something slightly different when I stumbled upon this answer.
My case was to keep a count based on a condition inside the list comprehension. Just in case it's useful to someone else, this is how I solved it:
import itertools
counter = itertools.count(0)
[(next(counter), x) for x in some_list if x != 'p']
In this way the counter will only be incremented when the condition is met and not at every iteration.
Upvotes: 22
Reputation: 40884
RTM: enumerate(['p', 'q', 'r', 's'], 1)
gives you a generator yielding (1, 'p'), (2, 'q'), (3, 'r'), (4, 's')
, convert it to list
to taste.
Upvotes: 4
Reputation: 5901
[(i, x) for i, x in enumerate(some_list, 1)]
[(i, x) for i, x in enumerate(some_list, 1) if i > 2]
or like this
[(i, x) for i, x in enumerate(some_list, 1) if x != 'p']
Most often you don't need to do this. Instead you just call enumerate(some_list, 1)
where the enumeration is needed, in a for
loop for example.
Upvotes: 67
Reputation: 4723
I guess you want something like numbering all items, independent of the level of nesting.
Maybe the following will help. Don't forget to create a new number
for each list comprehension. next
may be spelled __next__
in your version of Python.
>>> import itertools
>>> number = itertools.count().next
>>> [(number(), [(number(), x + 1) for x in range(y) if x % 2]) for y in range(10) if y % 3]
[(0, []), (1, [(2, 2)]), (3, [(4, 2), (5, 4)]), (6, [(7, 2), (8, 4)]), (9, [(10, 2), (11, 4), (12, 6)]), (13, [(14, 2), (15, 4), (16, 6), (17, 8)])]
Update: I know understand that you need different counters for each level of nesting. Just use more than one counter:
>>> number1 = itertools.count().__next__
>>> number2 = itertools.count().__next__
>>> print([(number1(), [(number2(), x + 1) for x in range(y) if x % 2]) for y in range(10) if y % 3])
[(0, []), (1, [(0, 2)]), (2, [(1, 2), (2, 4)]), (3, [(3, 2), (4, 4)]), (4, [(5, 2), (6, 4), (7, 6)]), (5, [(8, 2), (9, 4), (10, 6), (11, 8)])]
I.e., replace _
with number1()
as defined above, __
with number2()
, and so on. That's it.
Upvotes: 1
Reputation: 197
Would something like this help?
i = 1
y = range(10)
s = [(i + y.index(x), x**2) for x in y]
print s
>>> [(1, 0), (2, 1), (3, 4), (4, 9), (5, 16), (6, 25), (7, 36), (8, 49), (9, 64), (10, 81)]
I have a suspicion that there may be a better way to do this than through comprehensions though.
Upvotes: 0
Reputation: 29302
As already showed in the other answers the standard library gives you enumerate
, which means that you probably wont even need a list like:
[(1, 'P'), (2, 'Q'), (3, 'R'), (4, 'S')]
because every time you need to bind the letter with a number related to its position you can just call enumerate
.
Example:
>>> low = ['p', 'q', 'r', 's']
>>> upp = [c.upper() for c in low]
>>>
>>> for i,c in enumerate(upp, 1):
... print(i,c)
...
1 P
2 Q
3 R
4 S
This was just an example, maybe you actually need to that kind of list.
Upvotes: 7