I'm trying to make a function that draws a random number between 1 and 3 and calls it in another function in which if the number chosen in the drawing of the first function is 1, it returns a list with up to 3 options among the possible ones in the listOne, if it is 2 returns up to three possible ones from the listTwo and if it is 3 the same thing for the listThree, but the output is not coming out correctly
Thats my code:
from random import random, randint, sample, choices
import csv
class Test(object):
def __init__(self):
self.data = dict()
def foo1(self):
return randint(1,3)
def foo2(self):
n = 3
for i in range(n):
r = self.foo1()
if (r == 1):
listOne = ['A', 'B', 'C']
smpOne = choices(listOne, k = randint(1,3))
return smpOne
elif (r == 2):
listTwo = ['D', 'E', 'F']
smpTwo = choices(listTwo, k = randint(1,3))
return smpTwo
elif (r == 3):
listThree = ['G', 'H', 'I']
smpThree = choices(listThree, k = randint(1,3))
return smpThree
def concat(self, nToGenerate):
for i in range(nToGenerate):
self.data[i] = {'foo 1': self.foo1(), 'foo2': self.foo2()}
return self.data
if __name__ == '__main__':
try:
nToGenerate = int(input("How tests?"))
except ValueError:
print("Sorry, not a number")
test = Test()
myDict = test.concat(nToGenerate)
with open('test.csv', "w", newline='') as csvfile:
fieldnames = list(myDict.values())[0].keys()
writerFile = csv.DictWriter(csvfile, fieldnames)
writerFile.writeheader()
for key in myDict:
writerFile.writerow({field: myDict[key].get(field) or key for field in fieldnames})
and that is the output for 10 tests:
foo 1,foo2
2,['H']
1,"['A', 'B', 'B']"
2,"['A', 'A']"
2,"['F', 'E']"
**1,"['I', 'I']"**
2,['B']
2,"['H', 'G']"
2,['B']
2,"['D', 'F']"
3,"['I', 'G']"
As we can see there is an exit that has the number 1 drawn and comes out with a list ['I', 'I']
however, the letter I could only appear on a list whose draw with the number 3
When you call foo1 in:
def concat(self, nToGenerate):
for i in range(nToGenerate):
self.data[i] = {'foo 1': self.foo1(), 'foo2': self.foo2()}
...
And then again in:
def foo2(self):
n = 3
for i in range(n):
r = self.foo1()
...
Both invocations to foo1 return a new pseudo-random number. There is no reason to think that they should always be the same random number.
The easiest solution would be to simply have foo2 take an argument. This argument will be the pseudo-random number, which will be generated in concat and then passed to foo2. You can eliminate the for-loop in foo2, since it does nothing (you return during the first iteration anyway). You can eliminate the if-statements by creating a dictionary, mapping the three possible values of r to different lists:
def foo2(self, r):
return choices({
1: ["A", "B", "C"],
2: ["D", "E", "F"],
3: ["G", "H", "I"]
}[r], k=randint(1, 3))
You would also have to change concat. For each iteration of the for-loop, we generate a new random number. That random number becomes the value for the "foo 1" key-value pair, and gets passed into foo2 to get the value for the "foo 2" key-value pair:
def concat(self, nToGenerate):
for i in range(nToGenerate):
r = self.foo1()
self.data[i] = {"foo 1": r, "foo 2": self.foo2(r)}
return self.data
I believe the issue is arising because you are calling foo1 twice, once in foo2 and once when assigning it the self.data. These will result in two different results from the random integer function so there is no guarantee that the printed value will match the value passed to foo2. You could solve this by returning the foo1 value in foo2 along with your return in the form of a tuple and then use indices when assigning to self.data. Hope I correctly understood and was helpful.
Related
This question already has answers here:
Find all the keys cluster in a list
(2 answers)
Closed 3 years ago.
I have a list of list with strings in it:
list = [["a","b"],["c","d"],["a", "e"],["f","d"],["x","y"]]
Now i want to merge all lists, that have 1 similar item in it like this:
grouped_list = [["a", "b", "e"],["c","d","f"],["x","y"]]
my code is this til now:
list = [["a","b"],["b","c"],["d","e"],["x","y"]]
clist = list.copy()
result = []
counter = 0
del_list = []
def oneofsame(L1, L2):
counter = 0
for i in L1:
for j in L2:
if i == j:
counter += 1
if counter == 0:
return False
else:
return True
for l in list:
try:
del clist[clist.index(l)]
except:
pass
result.append([])
for i in l:
for cl in clist:
if oneofsame(l, cl):
for j in l:
if j not in result[counter]:
result[counter].append(j)
for j in cl:
if j not in result[counter]:
result[counter].append(j)
del_list.append(cl)
else:
result.append(cl)
del_list.append(cl)
for j in del_list:
del clist[clist.index(j)]
del_list = []
counter += 1
del_list = []
cresult = result.copy()
for i in range(len(cresult)-1, 0, -1):
if cresult[i] == []:
del result[i]
print(result)
but this code doesn't merge all of my example input (I can't paste my example input, because its sensitiv data)
Here is a way to do it.
For each pair:
if we find a group that contains one of the values, we append the pair to the group
if we find a second group that contains the other value, we merge the groups.
if we found no matching group, then our pair constitutes a new one.
def group_equals(lst):
groups = []
for pair in lst:
pair = set(pair)
equals_found = 0
for idx, group in enumerate(groups):
if group.intersection(pair):
equals_found += 1
if equals_found == 1:
# We found a first group that contains one of our values,
# we can add our pair to the group
group.update(pair)
first_group = group
elif equals_found == 2:
# We found a second group that contains the other one of
# our values, we merge it with the first one
first_group.update(group)
del groups[idx]
break
# If none of our values was found, we create a new group
if not equals_found:
groups.append(pair)
return [list(sorted(group)) for group in groups]
tests = [ [["a", "b"], ["c", "d"], ["b", "c"]], # all equal
[["a","b"],["c","d"],["a", "e"],["f","d"]],
[["a","b"],["c","d"],["a", "e"],["f","d"],["x","y"]]
]
for lst in tests:
print(group_equals(lst))
# [['a', 'b', 'c', 'd']]
# [['a', 'b', 'e'], ['c', 'd', 'f']]
# [['a', 'b', 'e'], ['c', 'd', 'f'], ['x', 'y']]
I hope my below code will solve your problem:
import itertools
import copy
lista = [["a","b"],["c","d"],["a", "e"],["f","d"],["x","y"]] #[["a","b"],["e","d1"],["a", "e"],["a","d"],["d","y"]]
def grouped_list(lista):
aa = []
bbc = copy.deepcopy(lista)
flag = False
for a, b in itertools.combinations(lista,2):
bb = a+b
if len(set(bb)) < len(bb):
flag = True
cc = list(set(bb))
cc.sort()
if cc not in aa: aa.append(cc)
if a in lista: lista.remove(a)
if b in lista: lista.remove(b)
if lista: aa = aa + lista
if not flag: return bbc
else: return grouped_list(aa)
print ("Grouped list -->", grouped_list(lista))
Feel free to ask/suggest anything in the above code.
I would like to make a function that takes a empty list and returns the values only once (even if the same value is entered several times).
Example
input from user:
list=[a,b,b,c,d,d,d,e,f]
The function returns then:
list=[a,b,c,d,e,f]
(does not have to be in chronological order, does not matter)
What I have done so far is:
def func1():
list=[]
def func1():
list1=[]
while(1):
val = input("Enter the value\n")
if val == 'exit':
return list(set(list1))
list1.append(int(val))
print(func1())
you can use set that returns unique value
In [44]: def func1(l):
...: return [x for x in set(l)]
...:
In [45]: l=['a','b','b','c','d','d','d','e','f']
...:
In [46]: func1(l)
Out[46]: ['a', 'c', 'b', 'e', 'd', 'f']
In [47]:
You just simply make it below
def func1(mylist):
return list(set(mylist))
and call your method as :
newList = func1([1,1,2,2,3,4,5])
I have a function where depending on the type t a different function needs to be applied. I tried the following, but it does not work in a list context.
def compute(d, t='c'):
"""
d is the computing variable
t is the type, can be a list
"""
if t == 'a':
p = fa(d)
elif t == 'b':
p = fb(d)
else:
p = fc(d)
return p
For instance, t could be
t = ['a', 'b', 'a' , 'c', 'b']
and should return
p = [fa(d), fb(d), fa(d), fc(d), fb(d)]
Any suggestions?
Cheers,
Mike
This should work
def compute(d, t='c'):
"""
d is the computing variable
t is the type, can be a list
"""
l = []
for x in t:
if t == 'a':
l.append(fa(d))
elif t == 'b':
l.append(fb(d))
else:
l.append(fc(d))
if len(l) == 1:
return l[0]
else:
return l
compute(d, ['a', 'b', 'a' , 'c', 'b'])
It processes everything that is in t, be it a single item or a list.
Be aware that it returns the return values of the called functions, not the functions and their arguments
I am probably approaching this wrong, but would appreciate being straightened out.
I would like to be able to use both the values and the names of some attributes of a class
Sample:
class DoStuff(object):
def __init__(self):
self.a="Alpha"
self.b="Beta"
self.c="Gamma"
def printStuff(self):
for thing in [self.a, self.b, self.c]:
print NAMEOFTHING, thing
What I want is:
a Alpha
b Beta
c Gamma
How can I get that?
Edit: Some confusion because my example showed me printing ALL the values. Instead I want this:
a Alpha
c Gamma
with the list for my print method just having 'a' and 'c' in it.
The way your class and for loop are set up, there is nothing you can put in place of NAMEOFTHING to get to the names of those variables. Here are a few alternatives on how you can modify your approach:
Use a dictionary instead of individual attributes, and then provide a list of keys in your for loop:
class DoStuff(object):
def __init__(self):
self.names = {"a": "Alpha",
"b": "Beta",
"c": "Gamma"}
def printStuff(self):
for name in ['a', 'b', 'c']:
print name, self.names[name]
Use the attribute names in your list and then use getattr():
class DoStuff(object):
def __init__(self):
self.a="Alpha"
self.b="Beta"
self.c="Gamma"
def printStuff(self):
for name in ['a', 'b', 'c']:
print name, getattr(self, name)
The closest you could get is:
for thing in ['a', 'b', 'c']:
print thing, getattr(self, thing)
Variables can have multiple names and aren't aware of their own name, so if you know it's 'a', then you can use getattr to resolve the lookup.
Another option (although not greatly different than above)
to_get = ['a', 'b', 'c']
from operator import attrgetter
blah = zip(to_get, attrgetter(*to_get)(self))
Following on Jon's answer, you might also find it helpful to set the list of attributes you want to include in the output as an optional argument:
def printStuff(self, included=['a', 'c']):
for thing in included:
print thing, getattr(self, thing)
which makes it easy to generate both outputs, by saying DoStuff().printStuff() to get just the values of a and c, or DoStuff().printStuff(['a', 'b', 'c']) to get all three. Of course, this allows for varying output—if it's an explicit design-goal that the set of fields being printed is invariant, this would be counterproductive.
# You can use __dict__
>>> class x:
>>> def __init__(self):
>>> self.a = 1
>>> self.b = 2
>>> self.c = 3
>>> self.d = 4
>>> def prnt(self):
>>> limit = "b", "c"
>>> return {k:v for (k, v) in self.__dict__.iteritems()if k in limit}
>>> r = x()
>>> print r.prnt()
{'b': 2, 'c': 3}
# __dict__ can be also done outside the class
limit = "b", "c"
print {k:v for (k, v) in r.__dict__.iteritems()if k in limit}
I'm creating a class where one of the methods inserts a new item into the sorted list. The item is inserted in the corrected (sorted) position in the sorted list. I'm not allowed to use any built-in list functions or methods other than [], [:], +, and len though. This is the part that's really confusing to me.
What would be the best way in going about this?
Use the insort function of the bisect module:
import bisect
a = [1, 2, 4, 5]
bisect.insort(a, 3)
print(a)
Output
[1, 2, 3, 4, 5]
Hint 1: You might want to study the Python code in the bisect module.
Hint 2: Slicing can be used for list insertion:
>>> s = ['a', 'b', 'd', 'e']
>>> s[2:2] = ['c']
>>> s
['a', 'b', 'c', 'd', 'e']
You should use the bisect module. Also, the list needs to be sorted before using bisect.insort_left
It's a pretty big difference.
>>> l = [0, 2, 4, 5, 9]
>>> bisect.insort_left(l,8)
>>> l
[0, 2, 4, 5, 8, 9]
timeit.timeit("l.append(8); l = sorted(l)",setup="l = [4,2,0,9,5]; import bisect; l = sorted(l)",number=10000)
1.2235019207000732
timeit.timeit("bisect.insort_left(l,8)",setup="l = [4,2,0,9,5]; import bisect; l=sorted(l)",number=10000)
0.041441917419433594
I'm learning Algorithm right now, so i wonder how bisect module writes.
Here is the code from bisect module about inserting an item into sorted list, which uses dichotomy:
def insort_right(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
If x is already in a, insert it to the right of the rightmost x.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if x < a[mid]:
hi = mid
else:
lo = mid+1
a.insert(lo, x)
If there are no artificial restrictions, bisect.insort() should be used as described by stanga. However, as Velda mentioned in a comment, most real-world problems go beyond sorting pure numbers.
Fortunately, as commented by drakenation, the solution applies to any comparable objects. For example, bisect.insort() also works with a custom dataclass that implements __lt__():
from bisect import insort
#dataclass
class Person:
first_name: str
last_name: str
age: int
def __lt__(self, other):
return self.age < other.age
persons = []
insort(persons, Person('John', 'Doe', 30))
insort(persons, Person('Jane', 'Doe', 28))
insort(persons, Person('Santa', 'Claus', 1750))
# [Person(first_name='Jane', last_name='Doe', age=28), Person(first_name='John', last_name='Doe', age=30), Person(first_name='Santa', last_name='Claus', age=1750)]
However, in the case of tuples, it would be desirable to sort by an arbitrary key. By default, tuples are sorted by their first item (first name), then by the next item (last name), and so on.
As a solution you can manage an additional list of keys:
from bisect import bisect
persons = []
ages = []
def insert_person(person):
age = person[2]
i = bisect(ages, age)
persons.insert(i, person)
ages.insert(i, age)
insert_person(('John', 'Doe', 30))
insert_person(('Jane', 'Doe', 28))
insert_person(('Santa', 'Claus', 1750))
Official solution: The documentation of bisect.insort() refers to a recipe how to use the function to implement this functionality in a custom class SortedCollection, so that it can be used as follows:
>>> s = SortedCollection(key=itemgetter(2))
>>> for record in [
... ('roger', 'young', 30),
... ('angela', 'jones', 28),
... ('bill', 'smith', 22),
... ('david', 'thomas', 32)]:
... s.insert(record)
>>> pprint(list(s)) # show records sorted by age
[('bill', 'smith', 22),
('angela', 'jones', 28),
('roger', 'young', 30),
('david', 'thomas', 32)]
Following is the relevant extract of the class required to make the example work. Basically, the SortedCollection manages an additional list of keys in parallel to the items list to find out where to insert the new tuple (and its key).
from bisect import bisect_left
class SortedCollection(object):
def __init__(self, iterable=(), key=None):
self._given_key = key
key = (lambda x: x) if key is None else key
decorated = sorted((key(item), item) for item in iterable)
self._keys = [k for k, item in decorated]
self._items = [item for k, item in decorated]
self._key = key
def __getitem__(self, i):
return self._items[i]
def __iter__(self):
return iter(self._items)
def insert(self, item):
'Insert a new item. If equal keys are found, add to the left'
k = self._key(item)
i = bisect_left(self._keys, k)
self._keys.insert(i, k)
self._items.insert(i, item)
Note that list.insert() as well as bisect.insort() have O(n) complexity. Thus, as commented by nz_21, manually iterating through the sorted list, looking for the right position, would be just as good in terms of complexity. In fact, simply sorting the array after inserting a new value will probably be fine, too, since Python's Timsort has a worst-case complexity of O(n log(n)). For completeness, however, note that a binary search tree (BST) would allow insertions in O(log(n)) time.
This is a possible solution for you:
a = [15, 12, 10]
b = sorted(a)
print b # --> b = [10, 12, 15]
c = 13
for i in range(len(b)):
if b[i] > c:
break
d = b[:i] + [c] + b[i:]
print d # --> d = [10, 12, 13, 15]
# function to insert a number in an sorted list
def pstatement(value_returned):
return print('new sorted list =', value_returned)
def insert(input, n):
print('input list = ', input)
print('number to insert = ', n)
print('range to iterate is =', len(input))
first = input[0]
print('first element =', first)
last = input[-1]
print('last element =', last)
if first > n:
list = [n] + input[:]
return pstatement(list)
elif last < n:
list = input[:] + [n]
return pstatement(list)
else:
for i in range(len(input)):
if input[i] > n:
break
list = input[:i] + [n] + input[i:]
return pstatement(list)
# Input values
listq = [2, 4, 5]
n = 1
insert(listq, n)
Well there are many ways to do this, here is a simple naive program to do the same using inbuilt Python function sorted()
def sorted_inserter():
list_in = []
n1 = int(input("How many items in the list : "))
for i in range (n1):
e1 = int(input("Enter numbers in list : "))
list_in.append(e1)
print("The input list is : ",list_in)
print("Any more items to be inserted ?")
n2 = int(input("How many more numbers to be added ? : "))
for j in range (n2):
e2= int(input("Add more numbers : "))
list_in.append(e2)
list_sorted=sorted(list_in)
print("The sorted list is: ",list_sorted)
sorted_inserter()
The output is
How many items in the list : 4
Enter numbers in list : 1
Enter numbers in list : 2
Enter numbers in list : 123
Enter numbers in list : 523
The input list is : [1, 2, 123, 523]
Any more items to be inserted ?
How many more numbers to be added ? : 1
Add more numbers : 9
The sorted list is: [1, 2, 9, 123, 523]
To add to the existing answers: When you want to insert an element into a list of tuples where the first element is comparable and the second is not you can use the key parameter of the bisect.insort function as follows:
import bisect
class B:
pass
a = [(1, B()), (2, B()), (3, B())]
bisect.insort(a, (3, B()), key=lambda x: x[0])
print(a)
Without the lambda function as the third parameter of the bisect.insort function the code would throw a TypeError as the function would try to compare the second element of a tuple as a tie breaker which isn't comparable by default.
This is the best way to append the list and insert values to sorted list:
a = [] num = int(input('How many numbers: ')) for n in range(num):
numbers = int(input('Enter values:'))
a.append(numbers)
b = sorted(a) print(b) c = int(input("enter value:")) for i in
range(len(b)):
if b[i] > c:
index = i
break d = b[:i] + [c] + b[i:] print(d)`