Iterating over dictionaries in order and printing the values [duplicate] - python-2.7

This question already has answers here:
How to keep keys/values in same order as declared?
(13 answers)
Closed 8 years ago.
prices = {
"banana": 4,
"apple": 2,
"orange": 1.5,
"pear": 3
}
stock = {
"banana": 6,
"apple": 0,
"orange": 32,
"pear": 15
}
for key in stock:
print key
print 'prince: %s' % prices[key]
print 'stock: %s' % stock[key]
The output when I run this code is as follows:
orange
prince: 1.5
stock: 32
pear
prince: 3
stock: 15
banana
prince: 4
stock: 6
apple
prince: 2
stock: 0
Why is it not printing according the order in which the elements appear in the dictionary? For example, why isn't 'banana' printed first, as follows:
banana
price: 4
stock: 6

Python dictionaries are unordered, meaning they won't remember the order you put elements in. This post has some answers that may be helpful to you if you are looking for more information on dictionaries.
You can create an ordered dictionary-equivalent using the OrderedDict datatype.

Dictionaries don't have an order. The point of a dictionary is to fetch items by a key which is provided by the programmer, so it doesn't make sense to maintain order; as long as it is quick and easy to fetch items by their key.
There is OrderedDict which will remember the order in which the keys were inserted.
For your case, it is better to rearrange your data:
stock = {'banana': {'price': 4, 'stock': 6}, 'apple': {'price': 2, 'stock': 0}}
for fruit, details in stock.iteritems():
print('{0}'.format(fruit))
print('Price: {0.price}'.format(details))
print('Stock: {0.stock}'.format(details))
You can also just use tuple, like this:
stock = {'banana': (4, 6), 'apple': (2, 0)}
for fruit, details in stock.iteritems():
price, stock = details
print('{0}'.format(fruit))
print('Price: {0}'.format(price))
print('Stock: {0}'.format(stock))

Dictionaries are an unordered set of key:value pairs. They have no notion of order.
You can check out OrderedDict if you would like an ordered dictionary. Ordered dictionaries are just like regular dictionaries but they remember the order that items were inserted. When iterating over an ordered dictionary, the items are returned in the order their keys were first added.
See: https://docs.python.org/2/library/collections.html
collections.OrderedDict

Related

How do I aggregate values in a python list or dictionary based on book title?

Currently, my data is like this or a dictionary, not aggregated by title:
{Great Expectations, Bookstore 1, $8.99},
{Great Expectations, Bookstore 2, $12.99},
{Great Expectations, Bookstore 3, $6.99},
{Tales from the Crypt, Bookstore 1, $5.99},
{The Sisterhood of the Traveling Pants, Bookstore 3, $8.99}
{Oprah: The Icon, Bookstore 2, $6.99}
{Oprah: The Icon, Bookstore 3, $9.99}
I'd like to create list of a dictionary of prices that's aggregated by title that looks like this:
[Great Expectations, {price1: $6.99, price2: $8.99, price3: $12.99}],
[Tales from the Crypt, {price1: $5.99}],
[The Sisterhood of the.., {price1: $6.99}],
[Oprah: The Icon, {price1: $6.99, price2: $9.99}]
Thanks for your help. I will eventually pass this data into a Django view as a list. There can be more than 3 prices, n prices. I'd also like to order the prices from smallest to largest.
Assuming your initial list of books will follow a standard pattern of the first element being the title, second the bookstore and third price, you could do something like this:
aggregrated_books = {}
for book in book_list:
if aggregrated_books[book[0]]:
aggregrated_books[book[0]].append(book[2])
else:
aggregrated_books[book[0]] = [book[2]]
This will output something like:
{'Great Expectations': [6.99, 8.99, 12.99],
'Tales from the Crypt': [5.99],
'The Sisterhood of the', [6.99],
'Oprah: The Icon', [6.99, 9.99]}
Depending on what your use case for this data is you'd be better off aggregating to a list of prices rather than a dictionary like you included in your question. You can easily add price1 price2 etc when iterating through the list later.

Nested Dictionary from Lists and Other Dictionaries

I am trying to make a master dictionary from preexisting lists and dictionaries. I am having a hard time getting it to work like I think it should.
I have a list of student names.
names=[Alice, Bob, Charles, Dan]
I then have 2 dictionaries that have info based on student ID number and another piece of info.
dict1={'100':9, '101:9, '102':11, '103':10} #the keys are student ID and the values are the grade level of the student. Alice is 100, Bob=101...
dict2={'100':9721234567, '101':6071234567, '103': 9727654321, '104':6077654321} #this dictionary gives the home phone number as a value using the student ID as a key.
How can I make a master dictionary to give me all of a student's information? Here's what I've tried based on reading answers to other questions.
Dicta=dict(zip(names, dict1))
Dictb=dict(zip(names, dict2))
Dict=dict(zip(Dicta, Dictb))
Here's the sort of answer I'd like to get.
>Dict[Alice]
>>>'100, 9, 9721234567'
#this is Alice's ID, grade level, and home phone
names is ordered, but the keys of a dict are unordered, so you can't rely on zip(names,dict1) to correctly match names with keys (student ID). For example:
>>> d = {'100':1,'101':2,'102':3,'103':4}
>>> d
{'102': 3, '103': 4, '100': 1, '101': 2} # keys not in order declared.
You need one more dict matching names to student ID. Below I've added an ordered list of IDs then create that dictionary. Then I use a dict comprehension to compute the combined dictionary.
names = ['Alice','Bob','Charles','Dan']
ids = ['100','101','102','103']
dictn = dict(zip(names,ids))
dict1={'100':9, '101':9, '102':11, '103':10}
dict2={'100':9721234567, '101':6071234567, '102': 9727654321, '103':6077654321}
Dict = {k:(v,dict1[v],dict2[v]) for k,v in dictn.items()}
print Dict
print Dict['Alice']
Output:
{'Bob': ('101', 9, 6071234567L), 'Charles': ('102', 11, 9727654321L), 'Alice': ('100', 9, 9721234567L), 'Dan': ('103', 10, 6077654321L)}
('100', 9, 9721234567L)

Sort nested dictionary in ascending order and grab outer key?

I have a dictionary that looks like:
dictionary = {'article1.txt': {'harry': 3, 'hermione': 2, 'ron': 1},
'article2.txt': {'dumbledore': 1, 'hermione': 3},
'article3.txt': {'harry': 5}}
And I'm interested in picking the article with the most number of occurences of Hermione. I already have code that selects the outer keys (article1.txt, article2.txt) and inner key hermione.
Now I want to be able to have code that sorts the dictionary into a list of ascending order for the highest number occurrences of the word hermione. In this case, I want a list such that ['article1.txt', 'article2.txt']. I tried it with the following code:
#these keys are generated from another part of the program
keys1 = ['article1.txt', 'article2.txt']
keys2 = ['hermione', 'hermione']
place = 0
for i in range(len(keys1)-1):
for j in range(len(keys2)-1):
if articles[keys1[i]][keys2[j]] > articles[keys1[i+1]][keys2[j+1]]:
ordered_articles.append(keys1[i])
place += 1
else:
ordered_articles.append(place, keys1[i])
But obviously (I'm realizing now) it doesn't make sense to iterate through the keys to check if dictionary[key] > dictionary[next_key]. This is because we would never be able to compare things not in sequence, like dictionary[key[1]] > dictionary[key[3]].
Help would be much appreciated!
It seems that what you're trying to do is sort the articles by the amount of 'hermiones' in them. And, python has a built-in function that does exactly that (you can check it here). You can use it to sort the dictionary keys by the amount of hermiones each of them points to.
Here's a code you can use as example:
# filters out articles without hermione from the dictionary
# value here is the inner dict (for example: {'harry': 5})
dictionary = {key: value for key, value in dictionary.items() if 'hermione' in value}
# this function just returns the amount of hermiones in an article
# it will be used for sorting
def hermione_count(key):
return dictionary[key]['hermione']
# dictionary.keys() is a list of the keys of the dictionary (the articles)
# key=... here means we use hermione_count as the function to sort the list
article_list = sorted(dictionary.keys(), key=hermione_count)

“Otherwise” argument of my IF-function applies to blank cells, but should ignore them. What can I add to my formula to stop it?

In my IF-function the “otherwise” argument should conduct the subtraction “6 - value”. It works fine for cells containing numbers, but unfortunately also works fine with blank cells. This results in a lot of cells with 6 (6 - 0 = 6) instead of empty cells.
In detail:
I want to import and select data collected in an online questionnaire.
I import my extract of the raw data in sheet “Sample” with the following formula:
=IF(LOOKUP(D$1,'Analysis'!$A$2:$A,'Analysis'!$G$2:$G)="No",FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0),ARRAYFORMULA(6-FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0)))
= If the question has not to be reversed (“No”), then import the values as they are, otherwise (if the question has to be reversed, “Yes”) subtract 6 - value.
Sheets in Google Spreadsheets:
“Import”: This sheet contains the raw data. For each person that participated in the study, there is a row with the corresponding answers (that is 1, 2, 3, 4 or 5 according to the rating scale in the questionnaire). Because not every person in the list started or completed the questionnaire, there are blank cells where no answers were registered and blank cells at the end of the sheet.
“Sample”: This sheet should contain an extract of the raw data for further analysis. It’s the sheet where the IF-formula is applied.
“Analysis”: This sheet contains informations concerning the questions, e.g. if the answers of some questions have to be reversed (reversed rating scale: 1 -> 5, 2 -> 4, 3 stays 3 and so on).
Coordinates:
Sheet “Sample”: Cell D$1, E$1, F$1 and so on contain the names of the questions (e.g. question_1).
Sheet “Analysis”: A2 to A contain the names of the questions.
Sheet “Analysis”: G2 to G contain the information if the answers of the questions have to be reversed. If the answers have to be reversed (“Yes”), the raw data needs to be adjusted with “6-” (6-5 = 1, 6-4 = 2, 6-3 = 3 and so on).
Sheet “Import”: A2 to A contains if there are any missing values. Zero means there are no missing values. Only data rows with no missing values should be imported.
Problem:
The formula works fine and displays the answers and reversed answers for the questions of interest. BUT at the end of the sheet “Sample” the columns continue with 6, 6, 6, 6, 6, 6, 6… (only for reversed questions); for not reversed questions the cells after the last valid import are blank.
Attempts to fix it:
I tried different variations of nested if-functions that unfortunately don’t have any effect, e.g.:
=IF(ISBLANK(Import!E2:I8)," ",IF(LOOKUP(D$1,Analysis!$A$2:$A,Analysis!$G$2:$G)="No",FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0),ARRAYFORMULA(6-FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0))))
or:
=IF(LOOKUP(D$1,Analysis!$A$2:$A,Analysis!$G$2:$G)="No",FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0),IF(Import!E2:E=" "," ",ARRAYFORMULA(6-FILTER(FILTER(Import!$A$2:$CV,Import!$A$1:$CV$1=D$1),Import!$A$2:$A=0))))
Alternatively, I could delete the cells with 6, 6, 6,… but that would be very time-consuming for all questionnaires.
Thanks for your help!
The following is the simple pattern
=IF(ISBLANK(A1),,6-A1)
This if A1 is blank, the will return a blank, otherwise, will return the result of 6-A1.
To apply the above to an open-ended reference, nest the above pattern inside FILTER in the following way:
=FILTER(IF(NOT(ISBLANK(A:A)),6-A:A,),LEN(A:A))
Replace A:A by a single column of the imported data, or a formula that returns a column of values.

Doctrine2 custom records order

I have simple query to get all pictures from database:
$this->createQueryBuilder('p')
->getQuery()
->getResult();
And I have array of ID's, which represents how they should be arranged in result array. How can I tell doctrine, to return these pictures in that specific order? For example, my "order array" is [3, 5, 9, 1, 10] and I want to get results in that particular order.
There are several ways to do this. If you're using postgresql you can try one of the raw queries here: ORDER BY the IN value list . If you're using mysql you can try the suggestions here: Ordering by the order of values in a SQL IN() clause
If you instead want to do it through straight Doctrine you could create a generic mapper function that orders the results. For very large result sets you may hit performance issues since you have O(n^2). But it's a little simpler than trying to make your own custom Doctrine function to handle it, relying on database implementation.
Say your query returns 5 results with the following ids: (95, 4, 1, 33, 35). Now say you want them returned as (1, 35, 4, 33, 95):
$entities = $em->getRepository('YourBundle:Picture')->findBy(/*some criteria*/);
$orderFunction = function($id) use ($entities)
{
foreach ($entities as $entity)
{
if ($entity->getId() == $id) {
return $entity;
}
}
};
$orderAs = array(1, 35, 4, 33, 95);
$orderedEntities = array_map($orderFunction, $orderAs);
Now your $orderedEntities array will be in the order you specified.