Mike
Mike

Reputation: 4405

How to Re-arrange items in a Python Dictionary during For Loop?

I am building a Python dictionary from a table in Excel. It's a Category:Name relationship. So, the first column in the spreadsheet is a category and the second column is the name of a file:

and so on...

I use this code to build the dictionary:

layerListDict = dict()
for row in arcpy.SearchCursor(xls):  

    # Set condition to pull out the Name field in the xls file. 
    # LayerList being the list of all the 'Name' from the 'Name' column built earlier in the script

    if str(row.getValue("Name")).rstrip() in layerList:

        # Determine if the category item is in the dictionary as a key already. If so, then append the Name to the list of values associated with the category 
        if row.getValue("Category") in layerListDict:
            layerListDict[row.getValue("Category")].append(str(row.getValue("Name")))
        # if not, create a new category key and add the associated Name value to it
        else:
            layerListDict[row.getValue("Category")] = [str(row.getValue("Name"))]

So, now I have a dictionary with Category as the key and a list of Names as the values:

{u'Forests': ['Tree Type', 'Soil Type'], u'Administrative': ['Cities', 'Buildings'], u'Mineral': ['Gold', 'Platinum'], u'Water': ['Watershed', 'Rivers', 'Lakes', 'Streams']}

I can now iterate over the sorted dictionary by key:

for k,v in sorted(layerListDict.iteritems()):
      print k, v

PROBLEM: What I would like to do is to iterate over the sorted dictionary with one caveat...I wanted to have the 'Mineral' key to be the very first key and then have the rest of the keys print out in alphabetical order like this:

Can anyone suggest how I can accomplish this?

I tried to set a variable to a sorted list, but it returns as a python list and I cannot iterate over the Python list by a key value pair anymore.

List2 = sorted(layerListDict.iteritems())

[u'Forests':['Tree Type', 'Soil Type'], u'Administrative': ['Cities', 'Buildings'], u'Mineral': ['Gold', 'Platinum'], u'Water': ['Watershed', 'Rivers', 'Lakes', 'Streams']]

Upvotes: 2

Views: 207

Answers (5)

Nacho
Nacho

Reputation: 461

Second shot at an answer. This is pretty different from my original, and makes some possibly wrong assertions, but I like the feel of it a lot better. Since you're able to determine all of the values in the "name" column (layerList), I'm going to assume you can do the same for the "categories" column. This code assumes you've placed your categories (including "Mineral") into an unsorted list called categories, and replaces the original code:

categories.sort()
categories = ["Mineral"] + [cat for cat in categories if cat != "Mineral"]

# Insert the categories into our dict with placeholder lists that we can append to
layerListDict = collections.OrderedDict([(cat, [],) for cat in categories])

for row in arcpy.SearchCursor(xls):
    if str(row.getValue("Name")).rstrip() in layerList:
        layerListDict[row.getValue("Category")].append(str(row.getValue("Name")))

Now you can just iterate over layerListDict.items().

Upvotes: 0

Nacho
Nacho

Reputation: 461

If you're just looking to print the key-value pairs, then the other solutions get the job done quite well. If you're looking for the resulting dictionary to have a certain order so that you can perform other operations on it, you should look into the OrderedDict class:

https://docs.python.org/2/library/collections.html#collections.OrderedDict

Objects are stored in the order that they are inserted. In your case, you would do something similar to the other answers first to define the order:

dict_tuples = sorted(layerListDict.items())
ordered_tuples = [("Mineral", layerListDict["Mineral"],)]
ordered_tuples += [(k, v,) for k, v in dict_tuples if k != "Mineral"]
ordered_dict = collections.OrderedDict(ordered_tuples) #assumes import happened above

Now you can do whatever you want with ordered_dict (careful with deleting then reinserting, see the link above). Don't know if that helps you more than some of the other answers (which are all pretty great!).

EDIT: Whoops, my recollection of the update behavior of OrderedDicts was a bit faulty. Fixed above. Also streamlined the code a little. You could potentially generate the tuples in your first for loop and then put them in the OrderedDict, too.

EDIT 2: Forgot that tuples are naturally sorted by the first element (thanks John Y), removed the unnecessary key param in the sorted() call.

Upvotes: 2

chepner
chepner

Reputation: 531065

An overly general solution:

import itertools
first = 'Mineral'
for k, v in itertools.chain([(first, layersListDict[first])], 
                            ((k,v) for (k,v) in layerListDict.iteritems() if k != first)):
    print k, v

or closer to my original incorrect solution:

for k, layersListDict[k] in itertools.chain((first,),
                                             (k for k in layerListDict
                                               if k != first)):
    print k, v

Upvotes: 3

Jeremiah
Jeremiah

Reputation: 1455

Keep a list of keys in the order you want to iterate over the map. Then iterate through the list, using the values as keys into the map.

Actually, after seeing the other solutions, I like chepner's answer with itertools.chain() better, especially if the list of keys is large, because mine will move things around in the list too much.

# sort the keys
keyList = sorted(keys(layerListDict))
# remove 'Mineral' from it's place
del keyList[keyList.index('Mineral')]
# Put it in the beginning
keyList = ['Mineral'] + keyList

# Iterate
for k in keyList:
    for v in layerListDict[k]:
        print k, v

Upvotes: 0

univerio
univerio

Reputation: 20518

print "Mineral", layerListDict.pop("Mineral")
for k, v in sorted(layerListDict.iteritems()):
    print k, v

If you don't want to modify layerListDict:

print "Mineral", layerListDict["Mineral"]
for k, v in sorted(layerListDict.iteritems()):
    if k != "Mineral":
        print k, v

Upvotes: 4

Related Questions