pythonic way for FIFO order in Dictionary - python

I am trying to populate a dictionary in python but I would like to preserve the order of the keys as they get in - exactly FIFO like a list would do it.
For example,
I read a file called animals.txt containing the following information:
animal\tconservation_status\n
dog\tdomesticated\n
tiger\tEN\n
panda\tEN\n
I.e.,
animals = {'dog':'dom','tiger':'EN', 'panda':'EN'}
>>> for el in animals:
... print el
...
tiger
dog
panda
And in a FIFO SHOULD have been dog, tiger, panda as they come out...
When I read it into a dictionary the order will not be preserved. I would like the order to be preserved such that when I do a for loop the FIRST IN is the FIRST OUT.
I.e.,
dog, then tiger, then panda.
Is there a nice way to do this without having to keep an external index or a more complex dictionary structure? Not sure, sorry for my naivity here...

Yes. You use a collections.OrderedDict instead of a regular dictionary.
>>> d = OrderedDict((x,x) for x in reversed(range(10)) )
>>> d
OrderedDict([(9, 9), (8, 8), (7, 7), (6, 6), (5, 5), (4, 4), (3, 3), (2, 2), (1, 1), (0, 0)])
>>> regular = dict((x,x) for x in reversed(range(10)))
>>> regular
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
Notice that the OrderedDict preserves the order whereas the regular dict does not.
>>> OrderedDict([('dog','dom'),('tiger','EN'), ('panda','EN')])
OrderedDict([('dog', 'dom'), ('tiger', 'EN'), ('panda', 'EN')])
Another gotcha is that you need to pass items to the constructor (or .update) in a way that preserves order. In other words, you can't pass keyword args to the constructor and expect order to be preserved:
>>> OrderedDict(dog='dom',tiger='EN',panda='EN') #doesn't preserve order
OrderedDict([('tiger', 'EN'), ('panda', 'EN'), ('dog', 'dom')])

Related

How to i make "rows" consiting of pairs from a list of objects that is sorted based on their attributes

I have created a class with attributes and sorted them based on their level of x, from 1-6. I then want to sort the list into pairs, where the objects with the highest level of "x" and the object with the lowest level of "x" are paired together, and the second most and second less and so on. If it was my way it would look like this, even though objects are not itereable.
for objects in sortedlist:
i = 0
row(i) = [[sortedlist[i], list[-(i)-1]]
i += 1
if i => len(sortedlist)
break
Using zip
I think the code you want is:
rows = list(zip(sortedList, reversed(sortedList)))
However, note that this would "duplicate" the elements:
>>> sortedList = [1, 2, 3, 4, 5]
>>> list(zip(sortedList, reversed(sortedList)))
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]
If you know that the list has an even number of elements and want to avoid duplicates, you can instead write:
rows = list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
With the following result:
>>> sortedList = [1,2,3,4,5,6]
>>> list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
[(1, 6), (2, 5), (3, 4)]
Using loops
Although I recommend using zip rather than a for-loop, here is how to fix the loop you wrote:
rows = []
for i in range(len(sortedList)):
rows.append((sortedList[i], sortedList[-i-1]))
With result:
>>> sortedList=[1,2,3,4,5]
>>> rows = []
>>> for i in range(len(sortedList)):
... rows.append((sortedList[i], sortedList[-i-1]))
...
>>> rows
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]

Zipping a set and a list in python

I'm trying to find duplicates in a list. I want to preserve the values and insert them into a tuple with their number of occurrences.
For example:
list_of_n = [2, 3, 5, 5, 5, 6, 2]
occurance_of_n = zip(set(list_of_n), [list_of_n.count(n) for n in set(list_of_n)])
[(2, 2), (3, 1), (5, 3), (6, 1)]
This works fine with small sets. My question is: as list_of_n gets larger, will I have to worry about arg1 and arg2 in zip(arg1, arg2) not lining up correctly if they're the same set?
I.e. Is there a conceivable future where I call zip() and it accidentally aligns index [0] of list_of_n in arg1 with some other index of list_of_n in arg2?
(in case it's not clear, I'm converting the list to a set for purposes of speed in arg2, and under the pretense that zip will behave better if they're the same in arg1)
Since your sample output preserves the order of appearance, you might want to go with a collections.OrderedDict to gather the counts:
list_of_n = [2, 3, 5, 5, 5, 6, 2]
d = OrderedDict()
for x in list_of_n:
d[x] = d.get(x, 0) + 1
occurance_of_n = list(d.items())
# [(2, 2), (3, 1), (5, 3), (6, 1)]
If order does not matter, the appropriate approach is using a collections.Counter:
occurance_of_n = list(Counter(list_of_n).items())
Note that both approach require only one iteration of the list. Your version could be amended to sth like:
occurance_of_n = list(set((n, list_of_n.count(n)) for n in set(list_of_n)))
# [(6, 1), (3, 1), (5, 3), (2, 2)]
but the repeated calls to list.count make an entire iteration of the initial list for each (unique) element.

Converting nested lists to dictionary

Hi please I try to make a dictionary out of the nested lists below and I get a TypeError. Please help me fix it to get the desired output as shown below. Thanks
n1 = [[1,2],[3,4]]
n2 = [[(5,7),(10,22)],[(6,4),(8,11)]]
output = {1:(5,7), 2:(10,22), 3:(6,4), 4:(8,11)}
D1 = {}
for key, value in zip(n1,n2):
D1[key] = value
print D1
TypeError: unhashable type: 'list'
Your approach didn't work, because when you zip n1 and n2, the result will be like this
for key, value in zip(n1,n2):
print key, value
# [1, 2] [(5, 7), (10, 22)]
# [3, 4] [(6, 4), (8, 11)]
So, key is a list. But, it is not hashable. So it cannot be used as an actual key to a dictionary.
You can chain the nested lists to get them flattened and then you can zip them together with izip
from itertools import chain, izip
print dict(izip(chain.from_iterable(n1), chain.from_iterable(n2)))
# {1: (5, 7), 2: (10, 22), 3: (6, 4), 4: (8, 11)}
The beauty of this method is that, it will be very memory efficient, as it doesn't create any intermediate lists. So, this can be used even when the actual lists are very large.
Perhaps not the most pythonic way, but it's short:
In [8]: dict(zip(sum(n1, []), sum(n2, [])))
Out[8]: {1: (5, 7), 2: (10, 22), 3: (6, 4), 4: (8, 11)}
The sum() trick, is used for flattening the list.
Try this:
from itertools import chain
n1 = [[1,2],[3,4]]
n2 = [[(5,7),(10,22)],[(6,4),(8,11)]]
print dict(zip(chain(*n1), chain(*n2))

how to implement a list of paragraph word lengths using a for loop

I've never used stackoverflow before, I usually stay over at the math and physics sections. I'm a reactor physicist, not a programmer, and this is literally the first week I've been toying with Python 2, so please don't chastise me.
I'm supposed to make a list "wordLenLi" that contains the lengths of the words from a small paragraph using a for loop. The short paragraph is in the batch file
This is what I've tried. I've also tried messing around with the append() method. This little dinky book doesn't do much justice.
st = '''April is the crueles month, breeding
Lilacs out of the dead land, mixing
Memory and desire, stirring
Dull roots with spring rain.'''
x = st.upper()
wordLi = x.split(' ')
for n in wordLi:
z = len(n)
WordLenli = z.split()
print wordLenLi
Below is a list comprehension. List comprehensions are essentially a powerful shorthand for writing for loops. A basic list comprehension takes the form [expr for variable in iterable]. It goes over each value in iterable, assigns it to variable and then stores the result of expr in a list. So
WordLenLi = [len(word) for word in st.split()]
print(WordLenLi)
Produces
>>>
[5, 2, 3, 7, 6, 8, 6, 3, 2, 3, 4, 5, 6, 6, 3, 7, 8, 4, 5, 4, 6, 5]
As a for loop it would look like this
WordLenLi = []
for word in st.split(): #for each word in a list of words
WordLenLi.append(len(word)) #insert the length of the word into WordLenLi
Alternativley, as a demonstration:
WordLenLi = [(word,len(word)) for word in st.split()]
print(WordLenLi)
>>>
[('April', 5), ('is', 2), ('the', 3), ('crueles', 7), ('month,', 6), ('breeding', 8), ('Lilacs', 6), ('out', 3), ('of', 2), ('the', 3), ('dead', 4), ('land,', 5), ('mixing', 6), ('Memory', 6), ('and', 3), ('desire,', 7), ('stirring', 8), ('Dull', 4), ('roots', 5), ('with', 4), ('spring', 6), ('rain.', 5)]
You could also have it shorter than the first comprehension:
WordLenLi = map(len,st.split())
Also, as per Jon Clement's suggestion, you way want to replace st.split() with something like this:
re.findall(r'\b[\w\d%s]+\b' % string.punctuation,st)
Which would require you to import the re and string modules via import re,string.
So I like HennyH's answer, but just so you don't get the impression that list comprehensions are theo nly possible answer, we also have :
for word in paragraph.split() :
print(word.len())
The problem with original was:
z = len(n)
WordLenli = z.split()
You were trying to "split" a number as if it was a string. The general lessons are:
Reducing the number of moving pieces reduces potential bugs.
It helps to remember what kind of thing each of the named objects is.
I think those principles work as well in physics as they do in programming, but it is easy to lose sight of them when trouble starts.

python - from matrix to dictionary in single line

matrix
is a list of lists with the same length. I've to return a dictionary of the form
{i:(l1[i],l2[i],...,lm[i])}
Where the key i is matched with a tuple the i'th elements
from each list.
Say
matrix=[[1,2,3,4],[9,8,7,6],[4,8,2,6]]
so the line:
>>> dict([(i,tuple(matrix[k][i] for k in xrange(len(matrix)))) for i in xrange(len(matrix[0]))])
does the job pretty well and outputs:
{0: (1, 9, 4), 1: (2, 8, 8), 2: (3, 7, 2), 3: (4, 6, 6)}
but fails if the matrix is empty: matrix=[]. The output should be: {}
How can i deal with this?
How about this instead:
>>> matrix = [[1,2,3,4],[9,8,7,6],[4,8,2,6]]
>>> dict(enumerate(zip(*matrix)))
{0: (1, 5, 4), 3: (4, 8, 6), 2: (3, 7, 2), 1: (2, 6, 8)}
>>> matrix = []
>>> dict(enumerate(zip(*matrix)))
{}
try changing part "len(matrix[0])"
This will try look up an index that doesn't exist if the matrix is empty.
instead make it
len(matrix[0]) if matrix else 0
If you want a simple solution (instead of knowing what's wrong with your one), I suggest you use a list instead of a dict. Seeing as a matrix is fairly static in terms of its size and keys.
This can be done simply by zip(*matrix)
>>> matrix = [[1,2,3,4],[9,8,7,6],[4,8,2,6]]
>>> dict_ = dict(enumerate(zip(*matrix)))
>>> list_ = zip(*matrix)
>>> for key_ in dict_:
... print dict_[key_] == list_[key_]
...
True
True
True
True

Categories