1

I'm hoping this is an easy one for those masters of for loops and dictionary lookups...but every attempt has so far yielded duplication of the count.

So, to begin, I have two loops:

for location in locations:
    print(location.service_type)

for service_type in service_types:
    print(service_type)

First output:

library
railway_station
school
cinema
school
supermarket
fire_station

Second output:

{'field': 'bus_station', 'choice': 'Bus Station', 'count': 0}
{'field': 'car_park', 'choice': 'Car Park', 'count': 0}
{'field': 'cinema', 'choice': 'Cinema', 'count': 0}
{'field': 'dentist', 'choice': 'Dentist', 'count': 0}
{'field': 'doctor', 'choice': 'Doctor', 'count': 0}
{'field': 'fire_station', 'choice': 'Fire Station', 'count': 0}
{'field': 'garden', 'choice': 'Public Garden', 'count': 0}
{'field': 'hospital', 'choice': 'Hospital', 'count': 0}
{'field': 'leisure_centre', 'choice': 'Leisure Centre', 'count': 0}
{'field': 'library', 'choice': 'Library', 'count': 0}
{'field': 'public_service', 'choice': 'Public Service', 'count': 0}
{'field': 'railway_station', 'choice': 'Railway Station', 'count': 0}
{'field': 'school', 'choice': 'School', 'count': 0}
{'field': 'supermarket', 'choice': 'Supermarket', 'count': 0}
{'field': 'woodland', 'choice': 'Woodland', 'count': 0}

What would be the most efficient way to add 1 to the count every time the first for loop's output matches the 'field' value in the second list of dictionaries outputted from the second for loop for each 'field'?

I've started with all manor of loops and lookups but nothing is really getting past understanding how to manipulate the two data structures together coherently. I'm just flat out stuck on this.

Expected output:

list_of_dicts = [
   {'field': 'bus_station', 'choice': 'Bus Station', 'count': 0}
   {'field': 'car_park', 'choice': 'Car Park', 'count': 0}
   {'field': 'cinema', 'choice': 'Cinema', 'count': 1}
   {'field': 'dentist', 'choice': 'Dentist', 'count': 0}
   {'field': 'doctor', 'choice': 'Doctor', 'count': 0}
   {'field': 'fire_station', 'choice': 'Fire Station', 'count': 1}
   {'field': 'garden', 'choice': 'Public Garden', 'count': 0}
   {'field': 'hospital', 'choice': 'Hospital', 'count': 0}
   {'field': 'leisure_centre', 'choice': 'Leisure Centre', 'count': 0}
   {'field': 'library', 'choice': 'Library', 'count': 1}
   {'field': 'public_service', 'choice': 'Public Service', 'count': 0}
   {'field': 'railway_station', 'choice': 'Railway Station', 'count': 1}
   {'field': 'school', 'choice': 'School', 'count': 2}
   {'field': 'supermarket', 'choice': 'Supermarket', 'count': 1}
   {'field': 'woodland', 'choice': 'Woodland', 'count': 0}
]

My best attempt, which kind of works:

service_types = []

_list = []
for location in locations:
    _list.append(location.service_type)

for field, choice in fields:
    service_types.append({ 'field': field, 'choice': choice, 'count': 0 })

new_service_types = []

for service_type in service_types:
    new_service_types.append({ 'field': service_type['field'], 'choice': service_type['choice'], 'count': _list.count(service_type['field']) })

?

  • Why not have the `service_types` be a dictionary with the `field` as the key instead of using an array? – Nick is tired Feb 19 '18 at 21:22
  • You already indicated this data was produced from Django queries. What queries were those? You can have the *database* produce this data for you instead. – Martijn Pieters Feb 19 '18 at 21:40
  • @NickA it doesn't work when passing it back in the context. –  Feb 19 '18 at 21:49
  • @Michael Roberts, How did it turn out? What did you end up doing? – matisetorm Feb 22 '18 at 01:02
  • I ended up sticking with the above... –  Feb 22 '18 at 09:30
  • @MichaelRoberts Cool. Counter is pretty great tool to have. All sorts of way to use it. And _significantly_ faster counting if you have any sizable task to accomplish see (https://stackoverflow.com/questions/27801945/surprising-results-with-python-timeit-counter-vs-defaultdict-vs-dict). Happy to help – matisetorm Feb 22 '18 at 22:38

3 Answers3

0

Use Counter class https://docs.python.org/3.6/library/collections.html

from collections import Counter

It is very fast and very easy to use. Although, it works best with iterables (as opposed to the way I wrap it in a list on line 4 here), the code sample will get you started.

(For instance, if you had access to a list of all location.service_type you could just do Counter(list) and boom. Done.

counter = Counter()
list_of_dicts = []
for location in locations:
    counter.update([location.service_type])
print(counter)

for service_type in service_types:
    if service_type in counter:
        service_types['count'] = counter[service_type]
#and because you have specific output reqs
for service_type in service_types:
    list_of_dicts.append(service_type)

Output:

{
   'cinema': 1,
   'fire_station': 1,
   'library': 1,
   'railway_station': 1,
   'school': 2,
   'supermarket': 1
}


[
   {'field': 'bus_station', 'choice': 'Bus Station', 'count': 0},
   {'field': 'car_park', 'choice': 'Car Park', 'count': 0},
   {'field': 'cinema', 'choice': 'Cinema', 'count': 1},
   {'field': 'dentist', 'choice': 'Dentist', 'count': 0},
   {'field': 'doctor', 'choice': 'Doctor', 'count': 0},
   {'field': 'fire_station', 'choice': 'Fire Station', 'count': 1},
   {'field': 'garden', 'choice': 'Public Garden', 'count': 0},
   {'field': 'hospital', 'choice': 'Hospital', 'count': 0},
   {'field': 'leisure_centre', 'choice': 'Leisure Centre', 'count': 0},
   {'field': 'library', 'choice': 'Library', 'count': 1},
   {'field': 'public_service', 'choice': 'Public Service', 'count': 0},
   {'field': 'railway_station', 'choice': 'Railway Station', 'count': 1},
   {'field': 'school', 'choice': 'School', 'count': 2},
   {'field': 'supermarket', 'choice': 'Supermarket', 'count': 1},
   {'field': 'woodland', 'choice': 'Woodland', 'count': 0}
]
matisetorm
  • 857
  • 8
  • 21
0

I suggest you this simple loop:

for service_type in service_types:
    field = service_type['field']
    if field in locations:
        service_type['count'] += locations.count(field)

print(service_types)

The list service_types gives the expected output.

Laurent H.
  • 6,316
  • 1
  • 18
  • 40
0

Here's a solution:

from collections import Counter

# 1. count the occurrence of locations
cnt = Counter(lcn.service_type for lcn in locations)


# 2a. if you don't want a duplicate you can just do this
for st in service_types:
    st['count'] += cnt[st['field']]

# 2b. OR, if you do want a duplicate into a new list
new_list = [dict(st, count=(st['count'] + cnt[st['field']])) 
                for st in service_types]

Here is the documentation on the Counter class.

black panda
  • 2,842
  • 1
  • 20
  • 28