1

I have the following simple data structures:

teams = [ { 'league_id': 1, 'name': 'Kings' }, { 'league_id': 1, 'name': 'Sharkls' }, { 'league_id': 2, 'name': 'Reign' }, { 'league_id': 2, 'name': 'Heat' } ]
leagues = [ { 'league_id': 1, 'name': 'League 1' }, { 'league_id': 2, 'name': 'League 2' } ]

And I have the following dict comprehension:

league_teams = { x['league_id']: [ t['name']
    for t in teams if t['league_id'] == x ['league_id'] ]
    for x in leagues }

Which yields:

{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}

Is there a simpler way using itertools or something to get that dict? This feels a little cumbersome.

200_success
  • 7,286
  • 1
  • 43
  • 74
Wells
  • 10,415
  • 14
  • 55
  • 85
  • Is it necessary to search in the `leagues`? Don't you want: `league_teams = { x['name']: [ t['name'] for t in teams if t['league_id'] == x['league_id'] ] for x in leagues }`? – Laurent LAPORTE Nov 20 '16 at 22:46
  • *This feels a little cumbersome*...this may be off topic on StackOverflow as we troubleshoot code that does not work. Consider posting on [CodeReview](http://codereview.stackexchange.com/). Frankly, I don't see any issue with code. – Parfait Nov 20 '16 at 22:52

3 Answers3

2

Here's an adaptation of Moinuddin Quadri's O(n+m) solution that catches the "empty league" case, and which incidentally does not require any modules to be imported. The dict output does double-duty as his league_ids set, and since it's pre-initialized, it does not need to be a collections.defaultdict:

output = { league['league_id']:[] for league in leagues }
for team in teams:
    if team['league_id'] in output:
        output[team['league_id']].append(team['name'])
print(output)

The output is:

{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}

jez
  • 14,867
  • 5
  • 37
  • 64
  • I presume a "Sharkl" is a small Bavarian shark. – jez Nov 20 '16 at 23:42
  • 1
    Yes it will work, but here also the complexity will be (m+(n*m)), because `if` internally iterates thorough the list in order to make a check – Moinuddin Quadri Nov 20 '16 at 23:45
  • To avoid the `if`, could instead use `try`... `except KeyError:pass` around the `append`. But I guess these O() calculations omit the dict key lookup too, in all cases. – jez Nov 20 '16 at 23:46
  • 1
    Yes, that will be fine if that is what you like. But that is not as per my definition of cleaner code. I just thought to share this with you: http://stackoverflow.com/a/2831242/2063361 – Moinuddin Quadri Nov 20 '16 at 23:48
1

You do not need itertools here, instead collections.defaultdict is better choice. Complexity of your solution is O(n*m) whereas with defaultdict, it will be O(n+m).

You can achieve what you want like:

from collections import defaultdict

# create set to store `league_id` in `leagues`. Set holds unique
# values and also searching in set is faster than in normal list
leagues_id = set([item['league_id'] for item in leagues])

my_dict = defaultdict(list)

for item in teams:
    if item['league_id'] in leagues_id:
        my_dict[item['league_id']].append(item['name'])

where at the end my_dict will hold the value:

{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}

Edit: In case you also want entry in my_dict for the league_id present in leagues, but not in teams, you need to explictly make entries like:

for leagues_id in leagues_ids:
     _ = my_dict[leagues_id]   # Will create empty list for such ids
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • Good solution, although one thing it won't do is record empty leagues (leagues that have a name and an id but for which no teams exist). The OP's code records them as empty lists associated with a league id. – jez Nov 20 '16 at 23:30
  • I guess you would be knowing on how to modify that in my current code :) – Moinuddin Quadri Nov 20 '16 at 23:33
  • @jez: Added an edit to the answer. If I were you, I would have do it like this :) – Moinuddin Quadri Nov 20 '16 at 23:42
0

Checking t['league_id'] == x['league_id'] looks not necessary.

You can simplify with:

import collections

league_teams = collections.defaultdict(list)
for t in teams:
    league_teams[t['league_id']].append(t['name'])

If you really want itertools for that:

import itertools

league_teams = {k: [t['name'] for t in g]
                for k, g in itertools.groupby(teams, key=lambda t: t['league_id'])}

But it will only work if the teams list is sorted.

Laurent LAPORTE
  • 21,958
  • 6
  • 58
  • 103