iterating in a dictionary.. (double iteration) python - python

I have a dictionary
example: A = {1:'one',2:'two' , 3: 'three}
so what I want is basically like a 2 for loops sort of stuff..
so that I can get the following order..
# 1 2
# 1 3
# 2 3
... and so on if more elements are there
so basically that o(n2) operation.. where we have a loop withing a loop
how do we achieve this in python dictionary.
I am having a hard time figuring that out..
for key in A.keys():
# how do i Access all the other keys..
# do something
Thanks

>>> import itertools as it
>>> A = {1:'one', 2:'two', 3: 'three'}
>>> list(it.combinations(A.keys(), 2))
[(1, 2), (1, 3), (2, 3)]

>>> import itertools
>>> list(itertools.combinations([1, 2, 3], 2))
[(1, 2), (1, 3), (2, 3)]

If you need to iterate over all pairs of keys, you can do it with simple for loops:
>>> d={1:'one',2:'two',3:'three'}
>>> for (x,y) in ((x,y) for x in d for y in d if x!=y):
... print x,y
Edit:
To avoid listing the same pair twice you can use a set to store the pairs before you iterate:
>>> for (x,y) in set(((min(x,y),max(x,y)) for x in d for y in d if x!=y)):
... print x,y
But this is getting a bit unwieldy, I would suggest using itertools.combinations as shown in dstromberg's answer.

Related

Find an element by inner tuple in a list of a tuple of tuples

Alright. So I've been through some SO answers such as Find an element in a list of tuples in python and they don't seem that specific to my case. And I am getting no idea on how to use them in my issue.
Let us say I have a list of a tuple of tuples; i.e. the list stores several data points each referring to a Cartesian point. Each outer tuple represents the entire data of the point. There is an inner tuple in this tuple which is the point exactly. That is, let us take the point (1,2) and have 5 denoting some meaning to this point. The outer tuple will be ((1,2),5)
Well, it is easy to figure out how to generate this. However, I want to search for an outer tuple based on the value of the inner tuple. That is I wanna do:
for y in range(0, 10):
for x in range(0, 10):
if (x, y) in ###:
print("Found")
or something of this sense. How can this be done?
Based on the suggestion posted as a comment by #timgen, here is some pseudo-sample data.
The list is gonna be
selectPointSet = [((9, 2), 1), ((4, 7), 2), ((7, 3), 0), ((5, 0), 0), ((8, 1), 2)]
So I may wanna iterate through the whole domain of points which ranges from (0,0) to (9,9) and do something if the point is one among those in selectPointSet; i.e. if it is (9, 2), (4, 7), (7, 3), (5, 0) or (8, 1)
Using the data structures that you currently are, you can do it like this:
listTuple = [((1,1),5),((2,3),5)] #dummy list of tuples
for y in range(0, 10):
for x in range(0, 10):
for i in listTuple:#loop through list of tuples
if (x, y) in listTuple[listTuple.index(i)]:#test to see if (x,y) is in the tuple at this index
print(str((x,y)) , "Found")
You can make use of a dictionary.
temp = [((1,2),3),((2,3),4),((6,7),4)]
newDict = {}
# a dictionary with inner tuple as key
for t in temp:
newDict[t[0]] = t[1]
for y in range(0, 10):
for x in range(0, 10):
if newDict.__contains__((x,y)):
print("Found")
I hope this is what you are asking for.
Make a set from the two-element tuples for O(1) lookup.
>>> data = [((1,2),3),((2,3),4),((6,7),4)]
>>> tups = {x[0] for x in data}
Now you can query tups with any tuple you like.
>>> (6, 7) in tups
True
>>> (3, 2) in tups
False
Searching for values from 0 to 9:
>>> from itertools import product
>>> for x, y in product(range(10), range(10)):
... if (x, y) in tups:
... print('found ({}, {})'.format(x, y))
...
found (1, 2)
found (2, 3)
found (6, 7)
If you need to retain information about the third number (and the two-element inner tuples in data are unique) then you can also construct a dictionary instead of a set.
>>> d = dict(data)
>>> d
{(1, 2): 3, (2, 3): 4, (6, 7): 4}
>>> (2, 3) in d
True
>>> d[(2, 3)]
4

Access individual elements of tuples of dictionary keys

Considering the code snippet below -
list1 = [1,2,3,4]
list2 = [1,2,3,4]
list3 = ['a','b','c','d']
dct = dict(zip(zip(list1,list2),list3))
print(dct)
gives me,
{(1, 1): 'a', (2, 2): 'b', (3, 3): 'c', (4, 4): 'd'}
Now,
print(dct.keys())
gives me,
dict_keys([(1, 1), (2, 2), (3, 3), (4, 4)])
How can i access first element of the above list of keys?
Something like -
dct.keys[0, 0] = 1
dct.keys[0, 1] = 1
dct.keys[1, 0] = 2
dct.keys[1, 2] = 2
and so on...
Remember that a dict is unordered, and that dict.keys() may change order.
That said, to access the first element of a list, as you said, you can use list[element_index]. If the elemnt is an iterable, do that again!
So it would be
dct_keys = list(yourdict.keys())
dct_keys[0][0] = 1
dct_keys[0][1] = 1
dct_keys[1][0] = 2
dct_keys[1][1] = 2
You need to first convert the dct.keys() output to a list, and then the problem reduces to simple list-of-tuples indexing. To convert your .keys() output to a list, there are multiple available ways (check this out). Personally, I find using list comprehension as one of the simplest and most generic ways:
>>> [key for key in dct.keys()]
[(1, 1), (2, 2), (3, 3), (4, 4)]
And now simply index this list of tuples as:
>>> [key for key in dct.keys()][0][0]
1
Hope that helps.

Most pythonic way to remove tuples from a list if first element is a duplicate

The code I have so far is pretty ugly:
orig = [(1,2),(1,3),(2,3),(3,3)]
previous_elem = []
unique_tuples = []
for tuple in orig:
if tuple[0] not in previous_elem:
unique_tuples += [tuple]
previous_elem += [tuple[0]]
assert unique_tuples == [(1,2),(2,3),(3,3)]
There must be a more pythonic solution.
If you don't care which tuple round you return for duplicates, you could always convert your list to a dictionary and back:
>>> orig = [(1,2),(1,3),(2,3),(3,3)]
>>> list(dict(orig).items())
[(1, 3), (2, 3), (3, 3)]
If you want to return the first tuple round, you could reverse your list twice and use an OrderedDict, like this:
>>> from collections import OrderedDict
>>> orig = [(1,2),(1,3),(2,3),(3,3)]
>>> new = list(OrderedDict(orig[::-1]).items())[::-1]
[(1, 2), (2, 3), (3, 3)]
These are not the most efficient solutions (if that's of great importance) , but they do make for nice idiomatic one-liners.
Some benchmarking
Note the difference in speed, and that should you not care which tuple round you return, the first option is much more efficient:
>>> import timeit
>>> setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
'''
>>> print (min(timeit.Timer('(list(dict(orig).items()))', setup=setup).repeat(7, 1000)))
0.0015771419037069459
compared to
>>>setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
from collections import OrderedDict
'''
>>> print (min(timeit.Timer('(list(OrderedDict(orig[::-1]).items())[::-1])',
setup=setup).repeat(7, 1000)))
0.024554947372323
The first option is nearly 15 times faster according to these speed tests.
That being said however, Saksham's answer is also O(n) and smashes these dictionary methods efficiency wise:
>>> setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
newlist = []
seen = set()
def fun():
for (a, b) in orig:
if not a in seen:
newlist.append((a, b))
seen.add(a)
return newlist
'''
>>> print (min(timeit.Timer('fun()', setup=setup).repeat(7, 1000)))
0.0004833390384996095
If you want the first containing a specific key to always be the one that appears in the final list:
list(reversed(collections.OrderedDict( reversed([(1,2),(1,3),(2,3),(3,3)])).items()))
Which results in:
[(1, 2), (2, 3), (3, 3)]
If you do not wish to store in an additional data structure, the time complexity O(n^2), as pointed out in the comments:
orig = [(1,2),(1,3),(2,3),(3,3)]
newlist = []
for (a, b) in orig:
if not any(x == a for x, y in newlist):
newlist.append((a, b))
print newlist # prints [(1, 2), (2, 3), (3, 3)]
A little book-keeping can reduce it to linear time:
orig = [(1,2),(1,3),(2,3),(3,3)]
newlist = []
seen = set()
for (a, b) in orig:
if not a in seen:
newlist.append((a, b))
seen.add(a)
print newlist # prints [(1, 2), (2, 3), (3, 3)]

checking if combination already exists from list comprehension

As part of learning Python I have set myself some challenges to see the various ways of doing things. My current challenge is to create a list of pairs using list comprehension. Part one is to make a list of pairs where (x,y) must not be the same(x not equal y) and order matters((x,y) not equal (y,x)).
return [(x,y) for x in listOfItems for y in listOfItems if not x==y]
Using my existing code is it possible to modify it so if (x,y) already exists in the list as (y,x) exclude it from the results? I know I could compare items after words, but I want to see how much control you can have with list comprehension.
I am using Python 2.7.
You should use a generator function here:
def func(listOfItems):
seen = set() #use set to keep track of already seen items, sets provide O(1) lookup
for x in listOfItems:
for y in listOfItems:
if x!=y and (y,x) not in seen:
seen.add((x,y))
yield x,y
>>> lis = [1,2,3,1,2]
>>> list(func(lis))
[(1, 2), (1, 3), (1, 2), (2, 3), (1, 2), (1, 3), (1, 2), (2, 3)]
def func(seq):
seen_pairs = set()
all_pairs = ((x,y) for x in seq for y in seq if x != y)
for x, y in all_pairs:
if ((x,y) not in seen_pairs) and ((y,x) not in seen_pairs):
yield (x,y)
seen_pairs.add((x,y))
Alternatively, you can also use generator expression (here: all_pairs) which is like list comprehension, but lazy evaluated. They are very helpful, especially when iterating over combinations, products etc.
Using product and ifilter as well as the unique_everseen recipe from itertools
>>> x = [1, 2, 3, 1, 2]
>>> x = product(x, x)
>>> x = unique_everseen(x)
>>> x = ifilter(lambda z: z[0] != z[1], x)
>>> for y in x:
... print y
...
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)

using state in python generator comprehensions?

Is there any way to convert this generator function into a relatively simple generator comprehension? (the following works for me, I just want to understand what possible options are)
def annotate(x):
n = 0
for item in x:
yield(item,n)
n = n + 1
use example:
>>> for line in annotate([3,4,5]):
... print line
...
(3, 0)
(4, 1)
(5, 2)
You can use enumerate:
comp = ((y,x) for x,y in enumerate(iterable))
demonstration:
>>> annotated = ((y,x) for x,y in enumerate(range(1,4)))
>>> for line in annotated: print line
...
(1, 0)
(2, 1)
(3, 2)
Although, usually you'd only use enumerate and just unpack in the opposite order. :^)

Categories

Resources