Related
An example:
list = [[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
So the first index inside a nested list decides which following numbers will be put into an unnested list.
[2, 1, 2, 3, 4] -> 2: so 1 and 2 gets picked up
[0, 4, 5] -> 0: no number gets picked up
[1, 8, 9] -> 1; number 8 gets picked up
Output would be:
[1, 2, 8]
This is what I have so far:
def nested_list(numbers):
if isinstance(numbers[0], list):
if numbers[0][0] > 0:
nested_list(numbers[0][1:numbers[0][0] + 1])
else:
numbers = list(numbers[0])
return numbers + nested_list(numbers[1:])
I try to get the list through recursion but something is wrong. What am I missing or could this be done even without recursion ?
You try using list comprehension with tuple unpacking here.
[val for idx, *rem in lst for val in rem[:idx]]
# [1, 2, 8]
NB This solution assumes you would always have a sub-list of size 1 or greater. We can filter out empty sub-lists using filter(None, lst)
list1=[[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
list2= []
for nested_list in list1:
for i in range(nested_list[0]):
list2.append(nested_list[i+1])
You can try List-comprehension:
>>> [sub[i] for sub in lst for i in range(1, sub[0]+1) ]
[1, 2, 8]
PS: The solution expects each sublist to be a non-empty list, else it will throw IndexError exception due to sub[0].
Another list comprehension
sum([x[1:x[0] + 1] for x in arr], [])
# [1, 2, 8]
Using builtin function map to apply the picking function, and using itertools.chain to flatten the resulting list of list:
def pick(l):
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( list(map(pick, ll)) )
# [[1, 2], [], [8]]
print( list(itertools.chain.from_iterable((map(pick, ll)))) )
# [1, 2, 8]
Or alternatively, with a list comprehension:
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8]
Two important notes:
I've renamed your list of lists ll rather than list. This is because list is already the name of the builtin class list in python. Shadowing the name of a builtin is very dangerous and can have unexpected consequences. I strongly advise you never to use the name of a builtin, when naming your own variables.
For both solutions above, the error-handling behaves the same: exception IndexError will be raised if one of the sublists is empty (because we need to access the first element to know how many elements to pick, so an error is raised if there is no first element). However, no exception will be raised if there are not enough elements in one of the sublists. For instance, if one of the sublists is [12, 3, 4], then both solutions above will silently pick the two elements 3 and 4, even though they were asked to pick 12 elements and not just 2. If you want an exception to be raised for this situation, you can modify function pick in the first solution:
def pick(l):
if len(l) == 0 or len(l) <= l[0]:
raise ValueError('in function pick: two few elements in sublist {}'.format(l))
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9], [12, 3, 4]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8, 3, 4]
print( [x for l in ll for x in pick(l)] )
# ValueError: in function pick: two few elements in sublist [12, 3, 4]
Why does the second code give different output than the first one ?
Using a for loop:
my_list = [1, 2, 3, 4, 2, 6, 2, 2, 7, 3, 8, 2]
uniques = []
for item in my_list:
if item not in uniques:
uniques.append(item)
print(uniques)
Output:
[1, 2, 3, 4, 6, 7, 8]
Using list comprehension:
my_list = [1, 2, 3, 4, 2, 6, 2, 2, 7, 3, 8, 2]
uniques = []
uniques = [item for item in my_list if item not in uniques]
print(uniques)
Output:
[1, 2, 3, 4, 2, 6, 2, 2, 7, 3, 8, 2]
The expression [item for item in my_list if item not in uniques] computes a new list based on the comprehension all at once. It then assigns the result to the name uniques. During the time the comprehension is running, uniques is an empty list, so the test if item not in uniques always returns True.
In the first version, uniques is referencing a list that is being actively updated, so it is able to meaningfully check for items already in the list.
As an aside, this is a very inefficient way to check for duplicates, because every time you write if item not in uniques:, the entire list is checked in linear time. A better alternative would be to use a set, which does fixed-time lookups using a hash-table.
in this code I'm trying to delete every repeated element in the list and just make all of the elements unique and not repeated, so when I run this code give me an error:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
repeat = 0
for i in range(len(myList)-1):
for j in range(len(myList)-1):
if myList[i]== myList[j]:
repeat+=1
if repeat>1:
del myList[j]
print("The list with unique elements only:")
print(myList)
the error which apppears is :
Traceback (most recent call last):
File "main.py", line 8, in <module>
if myList[i]== myList[j]:
IndexError: list index out of range
why is that happens and how can I solve it?
It is a really bad idea to modify an array while looping on it as you have no control on the way things are handled.
May I suggest these two solutions to your problem.
The first one is using set.
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
myList = list(set(myList))
print("The list with unique elements only:")
print(myList)
The other solution is using an other array
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
uniques = []
for number in myList:
if number not in uniques:
uniques.append(number)
print("The list with unique elements only:")
print(uniques)
You can convert list to set, it will automatically delete all of repeated elements
a = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
unique_list = list(set(a))
print(a)
Note: We again convert set to list
What is heppening here is that you are deleting some elements in your list, making it shorter.
Since your for loops are running for the lenght of your original list, you will eventuall try to access an index that no longer exists. This will cause you to get "list index out of range"
To see this for your self, you can add a print statement, like so:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
repeat = 0
for i in range(len(myList)-1):
for j in range(len(myList)-1):
print(i,j,len(myList))
if myList[i]== myList[j]:
repeat+=1
if repeat>1:
del myList[j]
Set data type in Python is used to remove duplicity. Whenever any iterator needs to be viewed with only the unique values in it, it can be converted into a set and that will remove all the duplicate values. For example:
lis=[2,2,3,4]
l=set(lis)
print(l)
Output:
{2, 3, 4}
It can be converted back into the list:
lis=[2,2,3,4]
l=set(lis)
print(l)
l=list(l)
print(l)
Output:
{2, 3, 4}
[2, 3, 4]
Similarly:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
s=set(myList)
l=list(s)
print(l)
Output:
[1, 2, 4, 6, 9]
Frozen sets can also be used for this purpose. Although; elements of the frozen set remain the same after creation i.e, they can't be modified unlike the elements of the set which are mutable(can be modified).
Hope this was helpful!
I'm using Python 2.7 and have the following:
my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I'd like create a 1-d list where the elements are ordered by position in sublist and then order of sublist. So the correct output for the above list is:
[1, 4, 7, 2, 5, 8, 3, 6, 9]
Here's my (incorrect) attempt:
def reorder_and_flatten(my_list):
my_list = [item for sublist in my_list for item in sublist]
result_nums = []
for i in range(len(my_list)):
result_nums.extend(my_list[i::3])
return result_nums
result = reorder_and_flatten(my_list)
This flattens my 2-d list and gives me:
[1, 4, 7, 2, 5, 8, 3, 6, 9, 4, 7, 5, 8, 6, 9, 7, 8, 9]
The first half of this list is correct but the second isn't.
I'd also like my function to be able to handle only 2 sublists. For instance, if given:
[[1, 2, 3], [], [7, 8, 9]
the correct output is:
[1, 7, 2, 8, 3, 9]
Any thoughts?
Thanks!
You're attempting to flatten, and then reorder, which makes things a lot harder than reordering and then flattening.
First, for your initial problem, that's just "unzip", as explained in the docs for zip:
>>> my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> list(zip(*my_list))
... [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
(In Python 2.7, you could just write zip(…) here instead of list(zip(…)), but this way, the same demonstration works identically in both 2.x and 3.x.)
And then, you already know how to flatten that:
>>> [item for sublist in zip(*my_list) for item in sublist]
[1, 4, 7, 2, 5, 8, 3, 6, 9]
But things get a bit more complicated for your second case, where some of the lists may be empty (or maybe just shorter?).
There's no function that's like zip but skips over missing values. You can write one pretty easily. But instead… there is a function that's like zip but fills in missing values with None (or anything else you prefer), izip_longest. So, we can just use that, then filter out the None values as we flatten:
>>> my_list = [[1, 2, 3], [], [7, 8, 9]]
>>> from itertools import izip_longest
>>> list(izip_longest(*my_list))
[(1, None, 7), (2, None, 8), (3, None, 9)]
>>> [item for sublist in izip_longest(*my_list) for item in sublist if item is not None]
[1, 7, 2, 8, 3, 9]
(In Python 3, the function izip_longest is renamed zip_longest.)
It's worth noting that the roundrobin recipe, as covered by ShadowRanger's answer, is an even nicer solution to this problem, and even easier to use (just copy and paste it from the docs, or pip install more_itertools and use it from there). It is a bit harder to understand—but it's worth taking the time to understand it (and asking for help if you get stuck).
result = [l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
i.e.
my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
# => [1, 4, 7, 2, 5, 8, 3, 6, 9]
my_list = [[1, 2, 3], [], [7, 8, 9]]
[l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
# => [1, 7, 2, 8, 3, 9]
The itertools module's recipes section provides a roundrobin recipe that would do exactly what you want. It produces a generator, but your expected behavior would be seen with:
# define roundrobin recipe here
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
def reorder_and_flatten(my_list):
return list(roundrobin(*my_list))
Your original code's main issue is that it looped over for i in range(len(my_list)):, extending with my_list[i::3]. Problem is, this ends up duplicating elements from index 3 onwards (index 3 was already selected as the second element of the index 0 slice). There are lots of other small logic errors here, so it's much easier to reuse a recipe.
This will be fairly performant, and generalize better than most hand-rolled solutions (it will round robin correctly even if the sublists are of uneven length, and it doesn't require second pass filtering or special handling of any kind to allow None as a value like zip_longest does).
If you are happy to use a 3rd party library, you can use NumPy and np.ndarray.ravel:
import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
res_a = A.ravel('F') # array([1, 4, 7, 2, 5, 8, 3, 6, 9])
For the case where you have one or more empty lists, you can use filter to remove empty lists:
B = np.array(list(filter(None, [[1, 2, 3], [], [7, 8, 9]])))
res_b = B.ravel('F') # array([1, 7, 2, 8, 3, 9])
Both solutions require non-empty sublists to contain the same number of items. If list conversion is necessary you can use, for example, res_a.tolist().
While these "black box" methods won't teach you much, they will be faster for large arrays than list-based operations. See also What are the advantages of NumPy over regular Python lists?
array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
array2 = [1, 2, 2, 2, 5, 6, 6, 6, 9]
temp = set(array2)
array1.remove(temp)
Traceback (most recent call last):
File "Sudoku V2.py", line 6, in <module>
array1.remove(temp)
ValueError: list.remove(x): x not in list
Try this:
array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
array2 = [1, 2, 2, 2, 5, 6, 6, 6, 9]
set(array1).difference(array2)
=> set([3, 4, 7, 8])
The above makes use of the difference() method of sets, which returns a new set with elements in the set that are not in the iterable(s) received as parameter. Notice that there's no need to convert array2 to a set for this to work.
Also be aware that by using sets, all duplicate elements will be removed and the original order of the iterables won't necessarily be preserved. If that's an issue, try this alternative solution:
[x for x in array1 if x not in array2]
=> [3, 4, 7, 8]
If you don't care about the order of elements in the result, or about duplicate elements, set.difference, as in Óscar López's answer, is exactly what you want.
If you do care about order and preserving duplicates, the simplest way is to just build a new list, filtering out the ones in array2:
set2 = set(array2)
array1 = [item for item in array1 if item not in set2]
If you need to destructively modify array1 in-place instead of building a new one, it's almost always simpler to just destructively replace the contents of array1 all at once:
array1[:] = [item for item in array1 if item not in set2]
If you really want to modify it element by element, the best way would be to iterate array1 backward, removing in-place:
set2 = set(array2)
for i, value in enumerate(array1)[::-1]:
if value in set2:
del array1[i]
Finally, you can do it the way flornquake suggested, but that will take quadratic time instead of linear, because it takes no advantage of the set; for each element in set2, it does a linear array1.remove.
Do you want array1 - array2?
set1 = set(array1)
set2 = set(array2)
set1.difference(set2) is what you want.
s -> sequence
s.remove(x) remove the first item from s where s[i] == x
remove raises ValueError when x is not found in s.
http://docs.python.org/3/library/stdtypes.html#index-23
None of the elements in array1 is a set. All the elements in array1 are integers.
The remove() method takes a single element of the list, so we have to call it once for each element:
for item in set(array2):
array1.remove(item)
Other methods such as Óscar's and abanert's are better though, they are more idiomatic and a lot more efficient.
This is three solution that can be help to your question:
array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
array2 = [1, 2, 2, 2, 5, 6, 6, 6, 9]
array = [i for i in array1 if i not in array2]
print(array)
o = list(set(array1) - set(array2))
print(o)
from collections import OrderedDict
p = OrderedDict()
p['array'] = list(set(array1) - set(array2))
print(*p.values())