Error when search values in Python - python

I have a linked list with the following values:
switch_list = [[4, 1, 2, 2],
[4, 2, 3, 2],
[3, 1, 1, 3],
[3, 2, 4, 2],
[1, 3, 3, 1],
[1, 2, 2, 1],
[2, 1, 1, 2],
[2, 2, 4, 1]]
My goal is to compare an integer with the first value of the all linked-list and then, return a new list with filtered values.
Example: I want all values in switch_list with the first number equals 4, then the function will returns:
[[4, 1, 2, 2], [4, 2, 3, 2],]
Here is my program:
def bucket(value, input):
output = []
for i in input:
if i[0][0] == value:
output.append(i)
return output
And here is the output error:
File "matrix_optimizate.py", line 63, in bucket
if i[0][0] == value:
TypeError: 'int' object has no attribute '__getitem__'

You're already iterating over the outer list, so there's no need for two index lookups.
for i in input:
if i[0] == value:
output.append(i)
Also, there's a much more elegant way to do this using filter:
def bucket(input, value):
return filter(lambda x: x[0] == value, input)
At which point you probably don't even need to have it as it's own function.
And lastly you could use a list comprehension as well:
[i for i in input if i[0] == value]

I am assuming that input is going to be your list of lists. In that case,
for i in input:
will give you each sub list as i during each iteration. By trying to access
i[0][0]
you are trying to access the first element of the element of the sublist. In your example
i would give [4, 1, 2, 2]
i[0] would give 4, and
i[0][0] would therefore not make sense
Please try i[0] instead.
Edit: Please note that this answer only serves to point out your current problem. dursk's answer provides other solutions to what you are trying to perform as a whole and those options are much more powerful (list comprehension is a fantastic tool, I would recommend looking into it).

Related

Check if two lists in list have common elements. Connect them if so, without duplicates

I am stuck on how to solve this problem.
Given a set of lists in a list, if any two sets of lists contain a common element, the two lists would be combined into one.
Suppose I have a set of lists in a list [[0, 1], [3, 6], [3, 9]]. Notice that [3, 6] and [3, 9] have a common element 3, so they are combined into [3, 6, 9], so how to convert this set of lists in a list into [[0,1], [3, 6, 9]]?
This is my current code but I am stuck.
for i in connected:
for j in connected:
a_set = set(i)
b_set = set(j)
if (a_set & b_set):
i.extend(j)
connected.remove(j)
Challenging! This is my approach:
def combine_commons(input: list) -> list:
combine_found = False
for ct_current, item_current in enumerate(input):
# try to find a element that shares item:
combine_into = None
for ct_search, item_search in enumerate(input):
if ct_current==ct_search: continue # can skip if i==j
if any(i in item_search for i in item_current):
# if any elements match, combine them.
combine_into = item_search
combine_found = True
break
if isinstance(combine_into, list):
input[ct_current] = list(set(item_current + combine_into)) # overwrite with new combined
del input[ct_search]
if combine_found: return combine_commons(input)
return input
print(combine_commons([[0, 1], [3, 6], [3, 9]]))
print(combine_commons([[1,2],[2,3],[2,5],[5,1]]))
# >>> [[0, 1], [9, 3, 6]]
# >>> [[1, 2, 3, 5]]
What it basically does is it loops twice through the list of lists. Then, for each i and j it checks if they have something in common. If they do, combine them into one (overwriting element i with the new long list and deleting element j). This then breaks the loop, so my solution was to check all the items again (looking for mergers) in a recursive fashion. Hope this helps!
Sometimes using the right data structures can make all the difference. Your problem seems to require something like an adjacency matrix to resolve. So, here is a quick way to do this using Graphs.
The intuition is as follows -
This is inspired by the approaches mentioned here. Here is the code which is using highly optimized inbuilt networkx functions.
import networkx as nx
def merge_lists(l):
islands = nx.connected_components(nx.from_edgelist(l))
return [list(i) for i in islands]
print(merge_lists([[0,1],[3,6],[3,9]]))
print(merge_lists([[1,2],[2,3],[2,5],[5,1]]))
print(merge_lists([[1,2],[2,3],[9,8],[5,6],[5,7]]))
[[0, 1], [9, 3, 6]] #case 1, [[0,1],[3,6],[3,9]]
[[1, 2, 3, 5]] #case 2, [[1,2],[2,3],[2,5],[5,1]]
[[1, 2, 3], [8, 9], [5, 6, 7]] #case 3, [[1,2],[2,3],[9,8],[5,6],[5,7]]
Any variations in the cases can be easily accommodated by modifying the graph created in the function.
Here is an idea for this particular case, whether it can handle other edge cases is another question but perhaps something you can build on
connected = [[0, 1], [3, 6], [3, 9]]
new_list = []
for i, v in enumerate(connected):
for j in v:
try:
if j in connected[i+1]:
new_list.append(sorted(list(set(connected[i] + connected[i+1]))))
connected.pop(i)
connected.pop(i)
break
except IndexError:
pass
connected += new_list
print(connected)

How do you sum together and merge multiple numbers in a list? (Python)

So, say I have the following list and variable i:
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 0, 0], [1, 2, 3, 2, 1] ]
i = 3
I would like to create a new list which will sum together and merge numbers from each sublist the ith element upwards to produce the following:
new_data = [ [1, 2, 5], [1, 2, 3], [1, 2, 6] ]
Where I have summed from each sublist the 3rd element upwards and merged the elements together. I would like to ask how, by using the standard library in Python, I can go about achieving this for an arbitrary (integer) value of i. I have the following idea in mind but I'm not sure how to put it into real Python code:
(pseudo-code)
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
You can slice your inner list by index and sum the rest with the one-liner:
>>> new_data = [row[:i-1] + [sum(row[i-1:])] for row in data]
>>> new_data
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
You can find a nice, "pythonic" one-liner in taras' answer:
new_data = [row[:i-1] + [sum(row[i-1:])] for row in data].
From pseudo-code to correct python code
I'll focus my answer on helping you transform your pseudo-code into python code.
Pseudo-code:
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
The first issue with your pseudo-code is that you are making a distinction between the list data and its sublists sublist, but you are not making a distinction between the list new_data and its sublists. Let me add a variable new_sublist in the loop:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.append(elements before the ith element)
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
The second issue with your pseudo code: you make two calls to .append in each iteration of the loop. However, these two calls are not similar: the first call is supposed to append several elements, whereas the second call is supposed to append one element. Python makes a distinction between the two operations; if you want to add more than one element at once, use .extend instead of .append. The code becomes:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend([elements before the ith element])
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
Finally we can turn your pseudo-code into proper python code, using a list slice to get the elements before the ith element, and builtin function sum along with a list slice to sum the ith element onwards:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend(sublist[:i])
new_sublist.append(sum(sublist[i:]))
new_data.append(new_sublist)
Note that in python, looping over a list to construct a new list is such a common operation that we use the compact and elegant list comprehension syntax to do it instead of a multiline for-loop:
new_data = [sublist[:i] + [sum(sublist[i:])] for sublist in data]
Relevant documentation
list.extend and list.append;
list slices;
builtin function sum;
list comprehensions.
You can do it like so:
def merger(data, pos):
pos -= 1 # 0 based, your i above is 1 based
# collection list for result
result = []
# go over each given inner part
for inner in data:
# add it up to the correct position
result.append(inner[:pos])
# extend it by the sum of the remainder values
result[-1].append(sum(inner[pos:]))
return result
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
print(merger(data,i))
Output:
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
or in short:
def merger(data, pos):
return [[inner[: pos - 1] + [sum(inner[pos - 1 :])] for inner in data]]
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
new_data = [sublist[:i-1] + [sum(sublist[i-1:])] for sublist in data]
print(new_data)
>>> [[1, 2, 5], [1, 2, 5], [1, 2, 5]]

Difference between a loop in a function call and listing all the arguments explicitly

I have a function that sorts a list of lists by the first list. When I use the function with the variables like so:
sort_lists(IN[0],IN[1],IN[2])
it works perfectly. Although, as I don't know how many lists my input contains, I want to use this as my variable:
sort_lists(IN[idx] for idx in range(len(IN)))
Although this returns a sorting of one list (the superlist). Why is there a difference between these variables, and how can I improve the code?
Here is the function if decisive (here IN[0] is the input with a number of sublists):
def sort_lists(*args):
zipped_list= zip(*sorted(zip(*args)))
return [list(l) for l in zipped_list]
OUT = sort_lists(data_sort[0],data_sort[1],data_sort[2])
I want to use this output:
OUT = sort_lists(data_sort[idx] for idx in range(len(IN[0])))
Two things to understand here:
*args will give you all function parameters as a tuple
IN[idx] for idx in range(len(IN)) is a generator expression
You can see how your inputs are different if you simply add print statement in your function:
def sort_lists(*args):
print(args)
zipped_list= zip(*sorted(zip(*args)))
return [list(l) for l in zipped_list]
Let the input list of lists be: lists = [[2, 1, 3], [1, 3, 4], [5, 4, 2]].
sort_lists(lists[0], lists[1], lists[2])
will print: ([2, 1, 3], [1, 3, 4], [5, 4, 2]). That's a tuple of inner lists.
Though, if you call it like this:
sort_lists(lists[idx] for idx in range(len(lists)))
or
sort_lists(sublist for sublist in lists)
this will print (<generator object <genexpr> at 0x0000007001D3FBA0>,), a one-element tuple of a generator.
You can make your function work with a generator by accepting only one parameter:
def sort_lists(arg):
zipped_list= zip(*sorted(zip(*arg)))
return [list(l) for l in zipped_list]
sort_lists(lists[idx] for idx in range(len(lists)))
# [[1, 2, 3], [3, 1, 4], [4, 5, 2]]
but I suggest to leave your function as is, and unpack your lists in the place where you call it instead:
>>> sort_lists(*lists)
[[1, 2, 3], [3, 1, 4], [4, 5, 2]]
Just change the function to accept list of lists, is it problem? This piece of code:
IN[idx] for idx in range(len(IN))
returns again list of lists
As Georgy pointed out, the difference is between the arguments being a generator or list. I would also like to point out that this is an opportunity to use and practice the map method. map applies the same function to each entry in a list. The function can be a built-in like sorted.
list_a = [[2, 1, 3], [1, 3, 4], [5, 4, 2]]
sorted_list_a = list(map(sorted, list_a)) # sorted is the python built-in function
print(sorted_list_a)
Returns:
[[1, 2, 3], [1, 3, 4], [2, 4, 5]]
You'll notice that you'll have to pass your map to the list function because map returns a map object, so you have to turn it into a list.
The map documentation is here. And a good example of it is here.

Mirroring rows in matrix with loops/recursion?

Given some matrix, I need to mirror all the rows in the matrix. For example
[[2, 1],
[4, 3]]
would become
[[1, 2],
[3, 4]]
I managed to do it for the (2 x 2)-case. But I'm having trouble mirroring something like this:
[[1, 2, 3, 4],
[1, 2, 3, 4]]
This has to become
[[4, 3, 2, 1],
[4, 3, 2, 1]]
I want to do this with loops/recursion. If I use recursion, I would probably have as basic step that the inner most elements get swapped first, and then from here on we would make the matrix bigger by also including the outer elements and swapping them too. However, I'm having trouble with the recursion step. After having swapped the inner most elements, I want to include the next to inner most elements in the matrix, and swap them too, and then continue like this until we reach the outer elements. How can this be implemented in code? This is what I did so far:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def mirror(matrix):
# This corresponds to the basic step. The two inner most elements get swapped.
if len(matrix) == 2:
for i in range(len(matrix)):
for j in range(len(matrix)):
# Store one element in a temporal variable
temp = matrix[i][j]
matrix[i][j] = matrix[i][len(matrix) - 1]
matrix[i][len(matrix)-1] = temp
return matrix
else:
# Recursion step
for i in range(len(matrix)):
for j in range(len(matrix)):
return (matrix + mirror(matrix[(len(matrix) // 2) - 1 : len(matrix)]))
The recursion step is wrong I think. I tried using the slice operator, but I'm not sure how this should be done correctly. Any help with this problem would be appreciated.
A recursive solution is pretty trivial, just recurse across the array reversing each subarray:
arr= [[2, 1],
[4, 3]]
def reve(l):
# if we have recursed across all sub arrays just return empty list
if not l:
return []
# else reverse the first current sublist l[0] and recurse on the remaining sublists
return [l[0][::-1]] + reve(l[1:])
print(reve(arr))
[[1, 2], [3, 4]]
Which can be written concisely as:
def reve(l):
return [l[0][::-1]] + reve(l[1:]) if l else []
If you wanted it inplace:
arr = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def reve(l):
if not l:
return
# call inplace list.reverse on each sublist
l[0].reverse()
return reve(l[1:])
reve(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
And lastly we can achieve what you want inplace with no slicing at all using iter with the special method __length__hint:
def reve(l):
if l.__length_hint__() == 0:
return
sub = next(l)
sub.reverse()
return reve(l)
reve(iter(arr))
print(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
Both functions might use the map function, but you can use a imperative for also. About my recursive approach, the else statement refers to all cases between the ultimate and second elements of the list, they are being concatenated until the first element is reached.
My recursive approach:
a = [[1, 2, 3],
[5, 6, 7]]
def mirror(matrix):
def revert(row):
if len(row) == 1:
return [row[0]]
else:
return [row[-1]] + revert(row[:-1]) # concatenates two lists.
return [revert(row) for row in matrix]
mirror(a)
My declarative approach:
def mirror(matrix):
def revert(row):
return row[::-1] # copies the array in reverse order
return list(map(revert, matrix)) #<-for python3, and just map(...) for python2
mirror(a)
Both functions outputs
[[3, 2, 1], [7, 6, 5]]
Actually, a more Pythonic way of doing that would be using list comprehensions. You can do that simply by:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
reversed_matrix = (i[::-1] for i in matrix)
reversed_matrix will be a generator expression. You may convert it into a list by replacing "()" with "[]" In the list comprehension.
i[::-1] reverses the array in-place using slice operator

Returning a list of list elements

I need help writing a function that will take a single list and return a different list where every element in the list is in its own original list.
I know that I'll have to iterate through the original list that I pass through and then append the value depending on whether or not the value is already in my list or create a sublist and add that sublist to the final list.
an example would be:
input:[1, 2, 2, 2, 3, 1, 1, 3]
Output:[[1,1,1], [2,2,2], [3,3]]
I'd do this in two steps:
>>> import collections
>>> inputs = [1, 2, 2, 2, 3, 1, 1, 3]
>>> counts = collections.Counter(inputs)
>>> counts
Counter({1: 3, 2: 3, 3: 2})
>>> outputs = [[key] * count for key, count in counts.items()]
>>> outputs
[[1, 1, 1], [2, 2, 2], [3, 3]]
(The fact that these happen to be in sorted numerical order, and also in the order of first appearance, is just a coincidence here. Counters, like normal dictionaries, store their keys in arbitrary order, and you should assume that [[3, 3], [1, 1, 1], [2, 2, 2]] would be just as possible a result. If that's not acceptable, you need a bit more work.)
So, how does it work?
The first step creates a Counter, which is just a special subclass of dict made for counting occurrences of each key. One of the many nifty things about it is that you can just pass it any iterable (like a list) and it will count up how many times each element appears. It's a trivial one-liner, it's obvious and readable once you know how Counter works, and it's even about as efficient as anything could possibly be.*
But that isn't the output format you wanted. How do we get that? Well, we have to get back from 1: 3 (meaning "3 copies of 1") to [1, 1, 1]). You can write that as [key] * count.** And the rest is just a bog-standard list comprehension.
If you look at the docs for the collections module, they start with a link to the source. Many modules in the stdlib are like this, because they're meant to serve as source code for learning from as well as usable code. So, you should be able to figure out how the Counter constructor works. (It's basically just calling that _count_elements function.) Since that's the only part of Counter you're actually using beyond a basic dict, you could just write that part yourself. (But really, once you've understood how it works, there's no good reason not to use it, right?)
* For each element, it's just doing a hash table lookup (and insert if needed) and a += 1. And in CPython, it all happens in reasonably-optimized C.
** Note that we don't have to worry about whether to use [key] * count vs. [key for _ in range(count)] here, because the values have to be immutable, or at least of an "equality is as good as identity" type, or they wouldn't be usable as keys.
The most time efficient would be to use a dictionary:
collector = {}
for elem in inputlist:
collector.setdefault(elem, []).append(elem)
output = collector.values()
The other, more costly option is to sort, then group using itertools.groupby():
from itertools import groupby
output = [list(g) for k, g in groupby(sorted(inputlist))]
Demo:
>>> inputlist = [1, 2, 2, 2, 3, 1, 1, 3]
>>> collector = {}
>>> for elem in inputlist:
... collector.setdefault(elem, []).append(elem)
...
>>> collector.values()
[[1, 1, 1], [2, 2, 2], [3, 3]]
>>> from itertools import groupby
>>> [list(g) for k, g in groupby(sorted(inputlist))]
[[1, 1, 1], [2, 2, 2], [3, 3]]
What about this, as you said you wanted a function:
def makeList(user_list):
user_list.sort()
x = user_list[0]
output = [[]]
for i in user_list:
if i == x:
output[-1].append(i)
else:
output.append([i])
x = i
return output
>>> print makeList([1, 2, 2, 2, 3, 1, 1, 3])
[[1, 1, 1], [2, 2, 2], [3, 3]]

Categories