Saving a key from a dictionary without value - python

I am representing a weighted graph as a dictionary where the key represents a vertex and the following lists represent the edges incident on the vertex (first number weight of edge and second number adjacent vertex):
wGraph = { 1 : [[2, 2],[3, 4],[3, 3]],
3 : [[3, 2],[5, 4],[7, 5]],
2 : [[2, 1],[4, 4],[3, 6]],
4 : [[3, 1],[4, 2],[1, 6],[5, 3]],
6 : [[3, 2],[1, 4],[8, 5]],
5 : [[7, 3],[8, 6],[9, 7]],
7 : [9, 5]}
I would like to save a random key without its value from the dictionary to a list called visited.
random_num = random.randrange(len(wGraph))
visited = []
How can I get the key from the dictionary according to the random number?

To make a random choice from some values, use random.choice. You want to choose from the keys of the dictionary, so that's exactly what you pass in:
random.choice(wGraph.keys())

The answer depends on the details -- how many keys do you have, are they always numbers 1...max, do you want to revisit keys you've already visited before, are graph nodes added over time or do they stay constant, will you eventually visit all keys or only a small percentage of them, and so on.
One idea: if your keys are always numbered sequentially as in your example, you can just use random_num + 1. You already know they are the keys of the dict and you don't have to construct anything special.
Another option is to randomize the key list just once:
>>> keys_to_visit = wGraph.keys()
>>> keys_to_visit.shuffle()
>>> first_key_to_visit = keys_to_visit.pop()
And just keep calling .pop() whenever you need a new one. This only works if you don't want to revisit nodes and the node list is constant.

import random
visited = []
def random_key():
wGraph = {1: [[2, 2], [3, 4], [3, 3]],
3: [[3, 2], [5, 4], [7, 5]],
2: [[2, 1], [4, 4], [3, 6]],
4: [[3, 1], [4, 2], [1, 6], [5, 3]],
6: [[3, 2], [1, 4], [8, 5]],
5: [[7, 3], [8, 6], [9, 7]],
7: [9, 5]}
random_key = random.choice(wGraph.keys())
if random_key not in visited:
visited.append(random_key)
print visited
for e in range(7):
random_key()

Related

Order the sub-lists in a nested list

I have a series of lists, and I want to combine them in a larger nested list. However, I want to order them in a certain way. I want the first sub-list to be the one whose first element is zero. Then i want the second sub-list to be the one whose first element is the same as the LAST element of the previous list.
For example, here's four sub-lists;
[0, 3], [7, 0], [3, 8], [8, 7]
I want to end up with this;
[[0, 3], [3, 8], [8, 7], [7,0]]
I can't for the life of me see the code logic in my head that would achieve this for me.
Can anyone help please?
UPDATE
Solved!
Many thanks to all who contributed!
I think of your list as being a collection of links which are to be arranged into a chain. Here is an approach which uses #quanrama 's idea of a dictionary keyed by the first element of that link:
links = [[0, 3], [7, 0], [3, 8], [8, 7]]
d = {link[0]:link for link in links}
chain = []
i = min(d)
while d:
link = d[i]
chain.append(link)
del d[i]
i = link[1]
print(chain) #[[0, 3], [3, 8], [8, 7], [7, 0]]
Another approach with a generator function:
links = [[0, 3], [7, 0], [3, 8], [8, 7]]
def get_path(links, *, start=0, end=0):
linkmap = dict(links)
key = start
while True:
link = linkmap[key]
yield [key,link]
key = link
if link == end:
break
print(list(get_path(links)))
print(list(get_path(links,start=3,end=3)))
# [[0, 3], [3, 8], [8, 7], [7, 0]]
# [[3, 8], [8, 7], [7, 0], [0, 3]]
You can try something like this:
source = [[0, 3], [7, 0], [3, 8], [8, 7]]
# Start at 0
last_val = 0
# this will be the output
l = []
while len(l)==0 or last_val!=0:
# Find the first value where the first element is last_val
l.append(next(i for i in source if i[0]==last_val))
# set last val to the second element of the list
last_val = l[-1][1]
print(l)

Generate a unique list of pairs from a set of values that does not match previous n pairings

I'm working in python for this problem.
Given a list of values that can increase or decrease in size between iterations (it will be padded to always be even)
users = [1,2,3,4,5,6,7,8]
Create a new random set of pairings for the users (order does not matter)
pairs_2 = [[2,5],[4,6],[3,8],[1,7]]
and ensure that the pairing does not overlap with the previous n sets of pairs
pairs_1 = [[7,10],[3,5],[1,6],[2,8],[4,9]]
pairs_0 = [[2,4],[3,6],[1,5]]
My current implementation just involves generating a random set of pairs and then doing a set intersection against the previous n pairings to see if it is unique or not, this obviously does not scale well nor guarantee a solution in any reasonable time.
I've also tried to instead generate all possible combinations of pairs, and then find the difference between that and all previous pairings, but then there is the problem of selecting exactly users/2 pairs from the list that contains all users. It feels like the first half of this solution is good as it guarantees only new pairs, but then requires some additional logic to select the pairs.
import random
myList = [[1,2,3,4,5,6],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8]]
oldList = []
for e in range(0,len(myList)):
oldList.append([])
for d in range(0,len(myList[e]),2):
while True:
f = False
a = random.choice(myList[e])
myList[e].remove(a)
#print(myList,e)
b = random.choice(myList[e])
myList[e].remove(b)
#print(myList,e)
for c in oldList:
if [a,b] in c:
myList[e].append(a)
myList[e].append(b)
#print("Runned")
f = True
if f:
f = False
continue
oldList[e].append([a,b])
break
print(oldList)
All the result will be in oldList , It depend on which one you put in first
On your example , you should put in
myList = [[1,2,3,4,5,6],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8]]
And the result will be:
[[[6, 3], [5, 4], [2, 1]], [[5, 2], [10, 8], [3, 4], [1, 9], [7, 6]], [[6, 1], [8, 5], [7, 3], [4, 2]]]
And to get different pair out according to your example
pair_0 = oldList[0]
pair_1 = oldList[1]
pair_2 = oldList[2]
and the result will be
pair_0 = [[6, 3], [5, 4], [2, 1]]
pair_1 = [[5, 2], [10, 8], [3, 4], [1, 9], [7, 6]]
pair_2 = [[6, 1], [8, 5], [7, 3], [4, 2]]

Why is a different outcome occurring on the following use of sort

I was doing a puzzle and where i had to add 2 lists having same length to a new list and sort the list by the second element of the list.
for x in range(n):
tmp.append([start[x],end[x]])
where start and end are lists containing equal elements and n is the length of start and end.
Now, idk why a difference / error occurs between the use of following code.
end.sort()
for x in range(n):
tmp.append([start[x],end[x]])
and
for x in range(n):
tmp.append([start[x],end[x]])
tmp.sort(key=lambda x:x[1])
EDIT:-
Input list
start=[1, 3, 0, 5, 8, 5]
end=[2, 4, 6, 7, 9, 9]
output by sorting first
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
output by sorting later
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
works fine for this list but doesn't work for a bigger array
(array contains 80 elements thats why not uploading here)
If you sort end first, you combine the original order of start with the sorted order of end.
If you combine the two lists first and then sort by the end element, the start elements will get reordered, too, as they "tag along" with their end partner. Consider
start = [1, 2, 3]
end = [3, 2, 1]
Now, sorting end and combining, you'll end up with:
start = [1, 2, 3]
end = [1, 2, 3]
# =>
tmp = [[1, 1], [2, 2], [3, 3]]
Combining first, however, produces:
tmp = [[1, 3], [2, 2], [3, 1]]
And sorting this by the second element, will shuffle the old start elements as well:
tmp.sort(key=lambda x:x[1])
# [[3, 1], [2, 2], [1, 3]]
Side note: Check out zip:
tmp = list(zip(start, end))

Checking for duplicates in list of list and sorting them

I have a table containing:
table = [[5, 7],[4, 3],[3, 3],[2, 3],[1, 3]]
and the first values represented in each list, (5,4,3,2,1) can be said to be an ID of a person. the second values represented (7,3,3,3,3) would be a score. What I'm trying to do is to detect duplicates values in the second column which is in this case is the 3s in the list. Because the 4 lists has 3 as the second value, i now want to sort them based on the first value.
In the table, notice that [1,3] has one as the first value hence, it should replace [4,3] position in the table. [2,3] should replace [3,3] in return.
Expected output: [[5,7],[1,3],[2,3],[3,3],[4,3]]
I attempted:
def checkDuplicate(arr):
i = 0
while (i<len(arr)-1):
if arr[i][1] == arr[i+1][1] and arr[i][0] > arr[i+1][0]:
arr[i],arr[i+1] = arr[i+1],arr[i]
i+=1
return arr
checkDuplicate(table)
The code doesn't fulfil the output i wanted and i would appreciate some help on this matter.
You can use sorted with a key.
table = [[5, 7], [4, 3], [3, 3], [2, 3], [1, 3]]
# Sorts by second index in decreasing order and then by first index in increasing order
sorted_table = sorted(table, key=lambda x: (-x[1], x[0]))
# sorted_table: [[5, 7], [1, 3], [2, 3], [3, 3], [4, 3]]
You should sort the entire list by the second column, using the first to break ties. This has the advantage of correctly grouping the threes even when the seven is interpersed among them, e.g. something like
table = [[4, 3],[3, 3],[5, 7],[2, 3],[1, 3]]
In Python, you can do it with a one-liner:
result = sorted(table, key=lambda x: (-x[1], x[0]))
If you want an in-place sort, do
table.sort(key=lambda x: (-x[1], x[0]))
Another neat thing you can do in this situation is to rely on the stability of Python's sorting algorithm. The docs actually suggest doing multiple sorts in complex cases like this, in the reverse order of the keys. Using the functions from operator supposedly speeds up the code as well:
from opetator import itemgetter
result = sorted(table, key=itemgetter(0))
result.sort(key=itemgetter(1), reversed=True)
The first sort will arrange the IDs in the correct order. The second will sort by score, in descending order, leaving the IDs undisturbed for identical scores since the sort is stable.
If you want to leave the list items with non-duplicate second elements untouched, and the ability to deal with the cases where multiple second items can be duplicate, I think you'll need more than the built-in sort.
What my function achieves:
Say your list is: table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 3], [2, 3], [1, 3]]
It will not touch the items [5, 7] and [8, 9], but will sort the remaining items by swapping them based on their second elements. The result will be:
[[5, 7], [3, 1], [8, 9], [6, 1], [1, 3], [2, 3], [3, 3], [4, 3]]
Here is the code:
def secondItemSort(table):
# First get your second values
secondVals = [e[1] for e in table]
# The second values that are duplicate
dups = [k for k,v in Counter(secondVals).items() if v>1]
# The indices of those duplicate second values
indices = dict()
for d in dups:
for i, e in enumerate(table):
if e[1]==d:
indices.setdefault(d, []).append(i)
# Now do the sort by swapping the items intelligently
for dupVal, indexList in indices.items():
sortedItems = sorted([table[i] for i in indexList])
c = 0
for i in range(len(table)):
if table[i][1] == dupVal:
table[i] = sortedItems[c]
c += 1
# And return the intelligently sorted list
return table
Test
Let's test on a little bit more complicated table:
table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 9], [3, 3], [2, 2], [2, 3], [1, 3]]
Items that should stay in their places: [5, 7] and [2, 2].
Items that should be swapped:
[6, 1] and [3, 1].
[8, 9] and [3, 9]
[4, 3], [3, 3], [2, 3], [1, 3]
Drumroll...
In [127]: secondItemSort(table)
Out[127]:
[[5, 7],
[3, 1],
[3, 9],
[6, 1],
[1, 3],
[8, 9],
[2, 3],
[2, 2],
[3, 3],
[4, 3]]

Using set in Python inside a loop

I have the following list in Python:
[[1, 2], [3, 4], [4, 6], [2, 7], [3, 9]]
I want to group them into [[1,2,7],[3,4,6,9]]
My code to do this looks like this:
l=[[1, 2], [3, 4], [4, 6], [2, 7], [3, 9]]
lf=[]
for li in l:
for lfi in lf:
if lfi.intersection(set(li)):
lfi=lfi.union(set(li))
break
else:
lf.append(set(li))
lf is my final list. I do a loop over l and lf and when I find an intersection between an element from l and another from lf, I would like to merge them (union)
But I can't figure out why this is not working. The first to elements of the list l are being inserted with the append command, but the union is not working.
My final list lf looks like [set([1, 2]), set([3, 4])]
It seems to be something pretty basic, but I'm not familiar with sets.
I appreciate any help
Thanks
The problem is here:
lfi=lfi.union(set(li))
You are not modifying the set. You are creating a new set which is then discarded. The original set is still in the lf array. Use update instead:
lfi.update(li)
This modifies the original set instead of creating a new one. The result after making this change:
[set([1, 2, 7]), set([9, 3, 4, 6])]
Here is another way to write the same thing
For sets,
& means intersection
|= means update
I have also used map(set,l) so that you are not recreating the same set over and over
l=[[1, 2], [3, 4], [4, 6], [2, 7], [3, 9]]
lf=[]
for li in map(set,l):
for lfi in lf:
if lfi & li:
lfi |= li
break
else:
lf.append(li)
l=[[1, 2], [3, 4], [4, 6], [2, 7], [3, 9]]
lf=[]
for li in l:
for i, lfi in enumerate(lf):
if lfi.intersection(set(li)):
lfi=lfi.union(set(li))
lf[i] = lfi #You forgot to update the list
break
else:
lf.append(set(li))

Categories

Resources