Adding things to the emptiest list? (python) - python

I am looking for a simple way to compare the content of multiple lists and find the one with the fewest variables.
Then I want to set the new variable to be added to that list.
Example:
list1 = [1,5,7,12,4,8]
list2 = [3,2,9,11,14,6,10]
list3 = [13,15,16]
In this I would want it to find list3 to be the shortest and append the new value to it.

Due to pythons min, max, and sort's key keyword argument this is fairly simple to do in python
min([list1, list2, list3..], key = len).append(item)
key corresponds to the function applied to each element and whose result is used for comparison in both min and max. in this case the function len (which retrieves the length of sequence objects like list and tuple and any class that defines __len__ ) is used.
from min.
The key argument specifies a one-argument ordering function like that used for list.sort()
from list.sort()
key specifies a function of one argument that is used to extract a comparison key from each list element (for example, key=str.lower).
example
>>> x = [11231]
>>> y = [1,2,3,4,5]
>>> z = [1,2,3,4,1,1]
>>> min([x,y,z], key = len)
[11231]

You could write a small function that checks the len of each list, then append to that list.
def add_to_shortest(lists, item):
min(lists, key = lambda i: len(i)).append(item)
For example
>>> add_to_shortest([list1, list2, list3], 5)
>>> list3
[13, 15, 16, 5]

Related

Python - how to take the max length from a value of dictionary? Without lambda

Let us say I have this function:
def frequent_base(self):
dict = {}
for i in range(len(self.items)):
if self.items[i].base not in dict:
dict[self.items[i].base] = [(self.items[i].value)]
else:
dict[self.items[i].base] += [(self.items[i].base)]
return max(len(dict[self.items]), key=len(d))
Now, I can make it complicated and build a function which returns me index and such..
but it is bad coding and bad habit and takes long time ( especially in a test ).
How do I take the length?
let us say I have:
key1 with length 3 of value ( key1 has 3 values )
key2 with length 4 of value ( key 2 has 4 values )
key3 with length 2 of value ( key 3 has 2 values )
How do I take, not the key itself, not the value itself, but the len of values of key? which is 4 in this case.
or how do I take the key itself and then say length of value of that key? But I want to use Max function, I need to understand how to use that function good, with the key.
I will write and make myself super clear:
dict[1] = [1,2,3]
dict[2] = [1,2,3,4,5]
dict[3] = [1,2,3,7,8,9,10]
dict = {1: [1,2,3], 2:[1,2,3,4,5], 3:[1,2,3,7,8,9,10]}
I wish to return not dict[3], not 3, not the list of dict[3] it self.
I wish to return the length of the dict[3], which is 7
def frequent_base(self):
dict = {}
for i in range(len(self.items)):
if self.items[i].base not in dict:
dict[self.items[i].base] = [(self.items[i].value)]
else:
dict[self.items[i].base] += [(self.items[i].base)]
def key_for_len(dictionary):
return dictionary[1]
return max(dict.items(), key= key_for_len)
I am received error
Only thing you seem to need is maximal length amongst the values of your dictionary. You can easily get all the values using d.values() (d.items() would give you (key, value) tuples, which are harder to compare). Now we can easily calculate lengths of each value with generator comprehension (very much like list comprehension, which would also work) - len(v) for v in d.values(). Now we have an iterable with all the lengths, so it's just a matter of finding the maximum -
max(len(v) for v in d.values())
Should you need to get key or value with maximum length, we'd have to take a slightly different approach - we can use max key = argument to specify how we decide which element in the iterable is maximal - it is obvious when we are trying to get a maximum from few numbers, but not when we try to decide if (1, 3) is bigger than (2, 2) - in such case, we need to create a function that maps our items to easily comparable things like a number. In this case, we'd have tuples of (key, value) and we are comparing length of value - thus our function would be
def lenOfValue(kv):
return len(kv[1]) # kv[1] - 2nd element of a (key, value) tuple
code(1)
Then we pass that to max:
print(max(d.items(), key = lenOfValue))
And we get (2, [3, 4, 5, 6])
Bonus: lambda
Lambdas can be used here, which are really just a shorthand that lets us skip defining whole another function that we will probably never use again.
This code would be functionally exactly the same.
print(max(d.items(), key = lambda kv: len(kv[1])))
code(2)
Lambdas are nothing very complicated, being just a notation for creating simple, one-liner functions without all the bother of a def block, return etc.
Because Python's functions are objects like nearly anything else, this piece of code:
lenOfValue = lambda kv: len(kv[1])
really is in no way different that our previously used more lengthy definition of:
def lenOfValue(kv):
return len(kv[1])
It saves us few words and shows us the middle step between code(1) and code(2).
a_dict = {'some_key':[67,30,10], 'another_key':[87]}
max({ (k,len(v)) for k,v in a_dict.items() })
('some_key', 3)

python OOP unexpected results

I have a list containing lists of objects. More specifically,
l = [ [X1,X2],[X3,X4]]
where X1,X2,X3,X4 are objects.
Now, I have a method in the class definition, that takes a list of objects, and modifies the attribute of the present object. Thus,
Class X:
def __init__(self,value=1):
self.value = value
def func (self,l):
total = 0
for x in l:
total += x.value
self.value = total
The problem I encounter is as follows. I have to apply the function func on elements of l[1] using elements of l[0]. However, when I do so, it turns out that the elements of l[0] are also getting changed. Thus, when I input
for obj in l[1]:
obj.func(l[0])
then I see that elements of l[0] have values that should ideally be assigned to l[1].
the list lis created as follows.
l0 = []
for i in range(2):
newx= X(i)
l.append(newx)
l=[]
l.append(l0)
l.append(l0)
What am I missing?
Correction: It seems python doesn't create new list objects every time I append an existing list.I basically need to copy the list using copy command. Hence, it is modifying the existing object.
l.append(l0)
l.append(l0)
Here you are appending the same list twice. Changes to objects in one list will be seen in the other because they are the same list.
To get the result you want, you must create two lists rather than reuse the first list.

How does this Lambda Expression work?

I'm familiar with simple lambda expressions. However I have this lambda expression in a book.
dd_pair = defaultdict(lambda: [0,0])
dd_pair[2][1] = 1 #now dd_pair contains {2: [0,1]}
I didn't declare any input variables, which is not how I learned Lambdas.
The previous example from the book has
dd_dict = defaultdict(dict)
dd_dict["Joel"]["City"] = "Seattle"
#this produces {"Joel" : { "City": "Seattle"}}
This makes sense, The Key is Joel, the nested Key is City, and the value is Seattle.
My two part question is, how does a lambda work with no input variables? and should I assume that the dd_pair[2][1] is to create a key 2 and at index 1 of the list set the value =1?
lambda: [0, 0] is exactly the same as:
def zero_pair():
return [0, 0]
that is, it is a function that takes no arguments and returns a len 2 array with the two entries set to 0. defaultdict takes a callable that takes no arguments and returns the value for a missing key so your second example (dd_pair = defaultdict(lambda: [0,0])) is the same as:
dd_pair = defaultdict(zero_pair)
Now, a dictionary in Python can take any value for its key (as long as they are hashable).
{0: 'integer', '0': 'string'}[0] # 'integer'
So when we index into dd_pair with a two, since the dictionary doesn't have any key at 2 our lambda (which is equivalent to zero_pair) is called and returns a list with two zeros in it. We then set the second element in that list to 1, mutating the list in place.
So yes, you're pretty much spot on when you say:
should I assume that the dd_pair[2][1] is to create a key 2 and at index 1 of the list set the value = 1

How do I properly pass an argument to a function

I'm from a C++ background so this problem seems a little absurd to me:
Let's suppose I have a function:
def scale(data, factor):
for val in data:
val *= factor
This doesn't work as intended, if I pass a list, it changes nothing, but
def scale(data, factor):
for index, val in enumerate(data):
data[index] *= factor
and lst = [val * factor for val in lst] works properly.
How does Python handle argument passing? How do I know if the actual reference, or alias is passed?
if you want to mutate the list, you need to reference the elements. This version uses map (it could be written using list comprehensions)
def scale(data, factor):
return map(lambda x : x*factor, data)
a lambda function is an anonymous function.
>>> (lambda x : x + 1) (5)
6
The x takes the place of the variable in this case 5 + 1
So in this case, we traverse the list applying the function f(x) -> x * factor to every element of the list. The original list is not mutated, but instead we return a new version.
In python basic data types are passed by value - for example int, str, bool etc are passed by value
Derived data types like classes, enum, list, dict are passed by reference.
In your example, the problem is how you use the for loop - not the function argument. If you do:
for val in lst:
val += 1
The values inside lst won't get updated because the val is not the same as lst[0], lst[1] and so on IF val is of the basic data types. So, even here, the val is copied by value.
Second, In your example with enumerate:
But when you loop over the enumerated list, you are using data[index] - which modifies the element in the actual list.
And finally, In your example with the generator:
lst = [val * factor for val in lst] - here the generator loops over every element and creates a new list which is again stored in lst. This is something like a = a + 2 but extended to lists.
This behaviour is so because the basic data types are passed by value and the derived data types like lists are passed by reference consider this
>>> x = 24
>>> x + 1
25
>>> x
24
but on the otherhand with a list
>>> y = [1, 2, 3, 4]
>>> y.remove(2)
>>> y
[1,3,4]
so you should always be careful to reassign values back when performing operations on them in the case of the basic data ypes and also be careful with datatypes that are passed by reference because you could accidentally modify a variable without knowing

Python dictionary creation error

I am trying to create a Python dictionary from a stored list. This first method works
>>> myList = []
>>> myList.append('Prop1')
>>> myList.append('Prop2')
>>> myDict = dict([myList])
However, the following method does not work
>>> myList2 = ['Prop1','Prop2','Prop3','Prop4']
>>> myDict2 = dict([myList2])
ValueError: dictionary update sequence element #0 has length 3; 2 is required
So I am wondering why the first method using append works but the second method doesn't work? Is there a difference between myList and myList2?
Edit
Checked again myList2 actually has more than two elements. Updated second example to reflect this.
You're doing it wrong.
The dict() constructor doesn't take a list of items (much less a list containing a single list of items), it takes an iterable of 2-element iterables. So if you changed your code to be:
myList = []
myList.append(["mykey1", "myvalue1"])
myList.append(["mykey2", "myvalue2"])
myDict = dict(myList)
Then you would get what you expect:
>>> myDict
{'mykey2': 'myvalue2', 'mykey1': 'myvalue1'}
The reason that this works:
myDict = dict([['prop1', 'prop2']])
{'prop1': 'prop2'}
Is because it's interpreting it as a list which contains one element which is a list which contains two elements.
Essentially, the dict constructor takes its first argument and executes code similar to this:
for key, value in myList:
print key, "=", value

Categories