editing a list of lists python3 - python

I have the following code
class Board:
def __init__(self, size=7):
self._size = size
self._list, self._llist =[],[]
for i in range (self._size):
self._list.append('_ ')
for j in range(self._size):
self._llist.append(self._list)
def printboard(self):
for i in range(self._size):
for j in range(self._size):
print(self._llist[i][j], end = ' ')
print('\n')
def updateboard(self,x,y,letter):
self._llist[x][y]=letter
self.printboard()
board = Board(3)
board.updateboard(0,0,'c')
and this prints
c _ _
c _ _
c _ _
instead of
c _ _
_ _ _
_ _ _
I can't see what is going wrong. Also, is there a simpler way to create the list of lists dynamically?
Thanks!

You are creating llist with the same list object, repeated multiple times. If you want each list in llist to be a separate, independent object (so that when you modify the contents only one list is changed) then you need to append a different copy to each. The easiest way to do this is to change:
self._llist.append(self._list)
to
self._llist.append(list(self._list))
Simpler code would be:
self._list = ['_ '] * self._size
self._llist = [list(self._list) for i in range(self._size)]

Related

Excel custom sorting with custom cmp

Im totaly new in excel nor in VBA.
I need to write a VBA macro that will sort dogs by total gained points and if the points are same then check each atribute (from left to right) and sort by these.
I wrote some (i think) working sort in python:
import random
from functools import cmp_to_key
class Structure:
def __init__(self,total,agility,speed,endurance,follow,enthusiasm):
self.total = total
self.agility = agility
self.speed = speed
self.endurance = endurance
self.follow = follow
self.enthusiasm = enthusiasm
def __str__(self):
return 'Structure(Total=' + str(self.total) + ' ,agility=' + str(self.agility) +' ,speed=' + str(self.speed) + ' ,endurance=' + str(self.endurance)+\
' ,follow=' + str(self.follow)+' ,enthusiasm=' + str(self.enthusiasm)+')'
def compare(item1, item2):
if item1.total < item2.total:
return -1
elif item1.total > item2.total:
return 1
else:
#Agility compare
if(item1.agility>item2.agility):
return 1
elif(item1.agility<item2.agility):
return -1
#Speed compare
if(item1.speed>item2.speed):
return 1
elif(item1.speed<item2.speed):
return -1
#Endurance compare
if(item1.endurance>item2.endurance):
return 1
elif(item1.endurance<item2.endurance):
return -1
#Follow compare
if(item1.follow>item2.follow):
return 1
elif(item1.follow<item2.follow):
return -1
#Enthusiasm compare
if(item1.enthusiasm>item2.enthusiasm):
return 1
elif(item1.enthusiasm<item2.enthusiasm):
return -1
return 0
def fill():
#total = random.randint(163,170)
total = 170
agility = 0
speed = 0
endu = 0
fol = 0
enth = 0
while(total!=agility+speed+endu+fol+enth):
agility = random.randint(20,40)
speed = random.randint(20,40)
endu = random.randint(20,40)
fol = random.randint(20,40)
enth = random.randint(20,40)
return [total,agility,speed,endu,fol,enth]
if __name__ == "__main__" :
list = []
for i in range(10):
k = fill()
list.append(Structure(k[0],k[1],k[2],k[3],k[4],k[5]))
for i in list:
print(i)
print("*********************** after sort *******************")
zoznam = sorted(list, key=cmp_to_key(compare),reverse=True)
for i in zoznam:
print(i)
but i have no idea how to write it in excel.
My idea is that i select total numbers and it will sort whole row. The "data structure" in excel looks like this:
For example as you can see (on top) both of them have total of 170, agility same so pass and speed is higher so this is why he is on top.
Thanks in advance
EDIT:
Thanks a lot gimix :) Because i need more than three keys and i want only to sort selected ones i "changed" a little bit a macro to:
Selection.Sort Key1:=Range("I1"), _
Order1:=xlDescending, _
Key2:=Range("J1"), _
Order2:=xlDescending, _
Key3:=Range("K1"), _
Order3:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("L1"), _
Order1:=xlDescending, _
Key2:=Range("G1"), _
Order2:=xlDescending, _
Key3:=Range("H1"), _
Order3:=xlDescending, _
Header:=xlNo
The thing is, it SEEMS like it is working but i dont know if it SHOULD be "sorted two times" like these, and if there wouldnt be some kind of "leaks" (unwanted behavior)
edit2:
Shouldnt it be rather like this ?
Selection.Sort Key1:=Range("K1"), _
Order1:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("J1"), _
Order1:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("I1"), _
Order1:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("H1"), _
Order1:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("G1"), _
Order1:=xlDescending, _
Header:=xlNo
Selection.Sort Key1:=Range("L1"), _
Order1:=xlDescending, _
Header:=xlNo
In VBA you have the sort method of the range object:
Range("A6:L11").Sort Key1:=Range("L1"), _
Order1:=xlDescending, _
Key2:=Range("G1"), _
Order2:=xlDescending, _
Key3:=Range("H1"), _
Order3:=xlDescending, _
Header:=xlNo
Key1 etc identify which column to use; Order1 etc tell if you want to sort from lowest to highest or the other way round (default is xlAscending so you need to specify this); finally Header tells if your data has a header row (in your case we use xlNo since you have non-data rows between the headers (row1) and the data (row6 and following).
Btw your Python code could be simpler: just create a tuple of total, agility and speed and use it as the key: no need for defining a function nor for calling cmp_to_key()

How to make a matrix that only holds None values?

I have to write a function empty_matrix that must return a list of lists (a matrix)
desired output:
empty_matrix(3,4)
returns a list with three lists each of length four:
[[None,None,None,None],[None,None,None,None],[None,None,None,None]]
What should I change in my code below???
def empty_matrix(row,column):
first_col = []
for x in range(len(matrix)):
sublist = matrix[x]
for y in range(len(sublist)):
first_col.append(matrix[x][y])
Using a list comprehension:
def empty_matrix(row, column):
return [[None for _ in range(row)] for _ in range(column)]
But to fix your code, you're using len on variables matrix and sublist that aren't defined, your indentation is off, try something like this if you don't want to use a list comprehension.
def empty_matrix(row, column):
matrix = []
for c in range(column):
column = []
for r in range(row):
column.append(None)
matrix.append(column)
return matrix

What is this bucket sort implementation doing?

This is my code for bucket sort in Python.
from random import randrange
def insertion_sort(aList):
for i in range(1, len(aList)):
for j in range(i, 0, -1):
if aList[j] < aList[j-1]:
aList[j], aList[j-1] = aList[j-1], aList[j]
return aList
def bucket_sort(aList):
buckets = [[]] * len(aList)
for index, value in enumerate(aList):
buckets_index = value * len(aList) // (max(aList) + 1)
buckets[buckets_index].append(value)
answer = []
for bucket in buckets:
answer.extend(insertion_sort(bucket))
# answer += insertion_sort(bucket)
print(buckets[0])
print("\n")
# return answer
aList = [randrange(10) for _ in range(100)]
print(aList)
print("\n")
answer = bucket_sort(aList)
#print(answer)
What is happening? When I run the code, I always find that the first list in buckets is already sorted and the other lists in buckets are all copies of it.
Do I need the insertion sort for each list?
What would I use the "answer" variable for?!
I'm mainly relying on this visualization.
One thing that i notice right off the bat is that you initialize your variable buckets as buckets = [[]] * len(aList). This makes a list of identical copies of the empty list. As such, any modification of this list is replicated in every element of buckets. Change this line to:
buckets = [[] for _ in xrange(len(aList))]
To check if the lists inside the list are separate object, you could check their id's:
print [id(x) for x in buckets]
This should print a list of unique numbers.
I think this bucket sort would be more efficient and is more pythonesque.
def bucket(k):
unique = list(set(k))
values = [k.count(uni) for uni in unique]
result = ([unique[uni] for i in range(values[uni])] for uni in range(len(unique)))
result = sum(result, [])
return result

Python and matrix of objects

I'm new to Python. I use version 3.3. I'm doing something wrong but I can't find.
I create tb1, an array of objects.
Content is initialized and then printed.
I create tb2, a second array.
The content of tb1 is then copied into tb2 while transposing rows and columns.
A second print shows that tb1 is altered. I can't understand why.
The problem do not happen with a matrix of integers.
Results of prints are:
123456789
123256369
#!/bin/python3
class Item:
n=0
m=0
class Tb:
items = [[Item() for i in range(3)] for j in range(3)]
tb1 = Tb()
for i in range(3):
for j in range(3):
tb1.items[i][j].n = i*3+j+1
# print original content of tb1
for i in range(3):
for j in range(3):
print( tb1.items[i][j].n, end="")
print()
tb2 = Tb()
for i in range(3):
for j in range(3):
tb2.items[j][i].n = tb1.items[i][j].n
# print content of tb1. It is altered
for i in range(3):
for j in range(3):
print( tb1.items[i][j].n, end="")
print()
class Tb:
items = [[Item() for i in range(3)] for j in range(3)]
Here you are creating a class variable items that will be shared by all instances of Tb. To create instance variables, use __init__:
class Tb:
def __init__(self):
self.items = [[Item() for i in range(3)] for j in range(3)]

Distributing numbers in SuDoku

I am creating a SuDuko Generator, and am trying to distribute my 1's to every single box. To do this, I have created a list with two items (acting a coordinates). After the function has run, it returns a list such as [a, 1]. I then want a quick way to assign a variable a1 to 1 (a1 would be the variable I would want if [a, 1] was returned and 1 because I'm trying to distribute my 1's). Is there a way to do this? If so how? Here is the source code:
import random
def func():
rows1 = ['a', 'b', 'c']
rows2 = ['d', 'e', 'f']
rows3 = ['g', 'h', 'i']
columns1 = [1, 2, 3]
columns2 = [4, 5, 6]
columns3 = [7, 8, 9]
letter1 = random.choice(rows1)
number1 = random.choice(columns1)
random1 = [letter1, number1]
rows1.remove(letter1)
columns1.remove(number1)
letter2 = random.choice(rows1)
number2 = random.choice(columns2)
random2 = [letter2, number2]
rows1.remove(letter2)
columns2.remove(number2)
letter3 = random.choice(rows1)
number3 = random.choice(columns3)
random3 = [letter3, number3]
columns3.remove(number3)
letter4 = random.choice(rows2)
random4 = [letter4, number4]
rows2.remove(letter4)
columns1.remove(number4)
letter5 = random.choice(rows2)
number5 = random.choice(columns2)
random5 = [letter5, number5]
rows2.remove(letter5)
columns2.remove(number5)
letter6 = random.choice(rows2)
number6 = random.choice(columns3)
random6 = [letter6, number6]
columns3.remove(number6)
letter7 = random.choice(rows3)
number7 = random.choice(columns1)
random7 = [letter7, number7]
rows3.remove(letter7)
letter8 = random.choice(rows3)
number8 = random.choice(columns2)
random8 = [letter8, number8]
rows3.remove(letter8)
letter9 = random.choice(rows3)
number9 = random.choice(columns3)
random9 = [letter9, number9]
return (random1, random2, random3) #etc
Thanks in advance
I then want a quick way to assign a variable a1 to 1 (a1 would be the variable I would want if [a, 1] was returned and 1 because I'm trying to distribute my 1's). Is there a way to do this?
Yes, there is a way to do this, but no, you do not want to. The right thing to do is this:
Instead of creating 81 different variables, create one data structure, which can be indexed.
The most obvious choice is a dict that uses (row, col) pairs as keys.
First, for a dict:
cells = {}
cells['a', '1'] = 1
The downside here is that the cells don't exist until you fill them in. You can pre-fill them like this:
cells = {(letter, digit): None for letter in 'abcdefghi' for digit in '123456789'}
Or you can use a defaultdict, or use the setdefault function in place of [].
You could also use some kind of explicit 2D object, like a list of lists, or a numpy.array, etc., but then you'll need some way to convert a letter and digit into a pair of array coordinates:
cells = [[None for _ in range(9)] for _ in range(9)]
def getcell(letter, digit):
return cells[letter - ord('a')][int(digit) - 1]
def setcell(letter, digit, value):
cells[letter - ord('a')][int(digit) - 1] = value
       
You may realize that this is starting to look complex enough that it might be worth wrapping up in a class. If so, you're right. Something like this:
class Board(object):
def __init__(self):
self.cells = [[None for _ in range(9)] for _ in range(9)]
def map_coords(self, letter, digit):
return letter - ord('a'), int(digit) - 1
def getcell(self, letter, digit):
row, col = self.map_coords(letter, digit)
return self.cells[row][col]
    def setcell(self, letter, digit, value):
row, col = self.map_coords(letter, digit)
self.cells[row][col] = value
Now you can just do this:
board = Board()
board.setcell('a', '1') = 1

Categories

Resources