So I am trying to delete elements from an array and do some calculations with this array. The thing is, I want to still keep a copy of the original array, so I made a new array and assigned it the values of the original array. The thing is, whenever I print out the length of the original and the new array, it gives me the same length, but it gives me the length of the new array. How do I make a new array with the same values as 'a', but without changing 'a'
a = [2, 4, 5, 7, 8, 9]
b = []
b = a
for _ in range(2):
m = min(b)
b[:] = (x for x in b if x != m)
print(len(b))
print(len(a))
Saying b = a just creates a new variable that references the existing list object. To make a copy of a list you could use the list() function or list slicing:
b = list(a)
b = a[:]
That said, to create a new list from an existing one filtering out some elements you could use a list comprehension:
from heapq import nsmallest
m = nsmallest(2, a)
b = [x for x in a if x not in m]
You are not copying - you are aliasing . You give a second name to the same array (which is a list btw). try b = a[:] this will copy it - only works if the lists content is immutable.
If your list contains other lists it will "copy" the reference to that list but not the nested lists contents - that would be a deepcopy().
If you want to remove the lowest 2 you could also resort to sorting and slicing (you list is already sorted so no sort needed)
a = [2, 4, 5, 7, 8, 9]
b = a[2:] // slice, ignore first 2 entries which are the lowest 2
c = sorted(a)[2:] // implicitly creates a new sorted list, ignore lowest 2
print(b)
print(c)
Output:
[5, 7, 8, 9]
[5, 7, 8, 9]
There is difference between list and list[:].
When reading, list is a reference to the original list, and list[:] shallow-copies the list.
When assigning, list (re)binds the name and list[:] slice-assigns, replacing what was previously in the list.
However, if the list elements are lists themselves, even list1 = list[:] has its problems. Consider:
>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = a[:]
>>> b[0].remove(2)
>>> b
[[1, 3], [4, 5, 6], [7, 8, 9]]
>>> a
[[1, 3], [4, 5, 6], [7, 8, 9]]
This happens because each list element being copied to b is a list itself, and this copying of lists involves the same problem that occurs with the normal list1 = list2. The shortest way out is to explicitly copy every list element this way:
>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b=[[j for j in i] for i in a]
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> b[0].remove(2)
>>> b
[[1, 3], [4, 5, 6], [7, 8, 9]]
>>> a
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Related
lst_a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7]]
My goal is to check all nested lists in lst_a if the first entry == first entry of any element in lst_b. If it's not than copy ONLY THAT sublist. In this example he wouldn't copy lst_a[0] but 1 and 2.
I tried to achieve my goal with list comprehension but it won't work.
zero = [x[0] for x in lst_a]
if zero not in lst_b:
# I don't know what to do here.
Creating a tuple or a dictionary isn't possible because the whole process is in a loop in which every second new data come in and I try to avoid copying duplicates to the list.
EDIT: lst_b should look like that after the whole process:
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Extract all the first elements from lst_b into a set so you can check membership efficiently. Then use a list comprehension to copy all the sublists in lst_a that match your criteria.
first_elements = {x[0] for x in lst_b}
result = [x for x in lst_a if x[0] not in first_elements]
It's a bit of a mouthful, but not too bad:
lst_b.extend(x for x in lst_a if not any(x[0] == y[0] for y in lst_b)
If you want a new list rather than modifying lst_b in place, then
lst_c = lst_b + [x for x in lst_a if not any(x[0] == y[0] for y in lst_b)]
In either case, we examine each sublist x in lst_a. any(x[0] == y[0] for y in lst_b) is True if the first element of the sublist is equal to the first element of any sublist in lst_b. If that's not true, then we'll include x in our final result.
Using any allows us to avoid checking against every sublist in lst_b when finding a single match is sufficient. (There are cases where this could be more efficient than first creating an entire set of first elements, as in #barmar's answer, but on average that approach is probably more efficient.)
Another way:
exclude=set(next(zip(*lst_b)))
lst_b+=[sl for sl in lst_a if sl[0] not in exclude]
>>> lst_b
[[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Explanation:
zip(*lst_b) is a generator of the inverse of the matrix lst_b, The * expands the sub lists and this creates a generator that yields [(1, 6, 9), (4, 5, 8), (7, 4, 7)] in turn.
next(zip(*lst_b) we only need the first element of that inverse: (1,6,9)
set(next(zip(*lst_b))) only need the uniq elements of that so turn into a set. You get {1, 6, 9} (order does not matter)
[sl for sl in lst_a if sl[0] not in exclude] filter on that condition.
lst_b+= extend lst_b with the filtered elements.
Profit!
There may be more efficient ways of doing this, but this accomplishes the goal.
>>> [a for a in lst_a if a[0] not in [b[0] for b in lst_b]]
[[4, 5, 6], [7, 8, 9]]
I am trying to split a list into multiple ones. The original list lst should be split on every element that is present in a second list, split_on.
Example:
lst = [1, 2, 3, 4, 5, 6, 7, 8]
split_on = [3, 4, 7]
should yield:
[[1,2,3],[3,4],[4,5,6,7],[7,8]]
Note that both lst and split_on do not contain duplicate elements and that any item of split_on is also an item of lst. Finally, the order of the elements of split_on can be random.
How about the following:
a = [1,2,3,4,5,6,7,8]
b = [4,3,7]
res = [[]]
for i in a:
res[-1].append(i)
if i in b:
res.append([i])
print(res) # [[1, 2, 3], [3, 4], [4, 5, 6, 7], [7, 8]]
Note that since b is only used for membership tests and the order does not matter, you could consider converting it to a set so that the approach scales better.
I want to append the first 80% of the values in two arrays into one new array. I can figure out a work around to this issue but I came across this problem when trying to do it in one line and I'm interested in knowing if it is possible.
# I create my two arrays
a = [1,2,3,4,5]
b = [6,7,8,9,10]
# Showing what slicing a and b in this way returns
# 4 in this case is equal to 80%
a[:4]
[1, 2, 3, 4]
b[:4]
[6, 7, 8, 9]
# Append 80% of b to 80% of a
a[:4] += b[:4]
# Print a
# There is a 5 at the end which I don't want to be there :(
a
[1, 2, 3, 4, 6, 7, 8, 9, 5]
I understand that what my one line is doing is telling python to insert 80% of b, 80% of the way through a, but not actually to only keep 80% of a.
Is there a way to do this without then having to remove the last 20% of a afterwards?
What you're writing is like a[:4] = a[:4] + b[:4], you're leaving the last index of a (i.e. 5), unchanged. You could think of it a bit like an insert (without creating a nested list), i.e.
>>> a = [1,2,3,4,5]
>>> a.insert(4, b[:4])
>>> a
[1, 2, 3, 4, [6, 7, 8, 9], 5]
The solution to modify the same list as you want is to assign not just to a[:4], but to the entire list:
>>> a[:] = a[:4] + b[:4]
>>> a
[1, 2, 3, 4, 6, 7, 8, 9]
a[:4] += b[:4]
With this equation, you're basically adding the first 4 elements of b[6,7,8,9] to a[1,2,3,4], leaving the last element of a[5] untouched. You can leave out the last index of a[5] by doing
c = a[:4] + b[:4]
# I create my two arrays
a = [1,2,3,4,5]
b = [6,7,8,9,10]
a=a.copy()[:4]+b.copy()[:4]
# output [1, 2, 3, 4, 6, 7, 8, 9]
shallow copy, a reference of object is copied in other object.
It means that any changes made to a copy of object do reflect in the original objec
t. In python, this is implemented using “copy()” function.
created a shallow copy of a and b then, i am taking first 4 elements from it and
appending b element with a .
Following can be used to add a slice to append to front of list.
>>> a = [5,6]
>>> a[0:0] = [1,2,3]
>>> a
[1,2,3,5,6]
what slice to use to append to the end of list.
If you really want to use slice, you can use the length of a:
a = [5, 6]
a[len(a):] = [1, 2, 3]
a
output:
[5, 6, 1, 2, 3]
But the simplest is to directly extend a:
a = [5, 6]
a += [1, 2, 3] # or a.extend([1, 2, 3])
I think you should consider extend():
>>> a = [1, 2, 3]
>>> a.extend([4, 5, 6])
>>> a
[1, 2, 3, 4, 5, 6]
Both + and += operators are defined for list, which are semantically
similar to extend.
list + list2 creates a third list in memory, so you can return the
result of it, but it requires that the second iterable be a list.
list += list2 modifies the list in-place (it is the in-place operator,
and lists are mutable objects, as we've seen) so it does not create a
new list. It also works like extend, in that the second iterable can
be any kind of iterable.
Time Complexity
Append has constant time complexity, O(1).
Extend has time complexity, O(k).
Iterating through the multiple calls to append adds to the complexity,
making it equivalent to that of extend, and since extend's iteration
is implemented in C, it will always be faster if you intend to append
successive items from an iterable onto a list.
↳ More Information
>>> a = [1, 2, 3]
>>> a[len(a):] = [4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
or
>>> a = [1, 2, 3]
>>> a += [4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
You have got short answer from Jeevaa and Reblochon Masque but if you want to use for loop then try this:
a = [5,6]
b = [1,2,3]
for val in b[::-1]:#Reverse b and insert it to a
a.insert(0,val)
print(a)
Output
[1,2,3,5,6]
I know that you are not supposed to remove an element of a list while iterating over it but I have to.
I'm trying to iterate over a list of lists and if I find a value in a list of my lists i need to remove it.
This is what I've tried so far.
dict[["A1","A2"],
["B1","B2"],
["C1","C2"]]
for i in range(len(dict)):
if dict[i][0]=="A1":
dict.pop(i)
But it's giving me an error of out of range.
How can I do it with list comprehensions or any other approach?
Do you mean this?
old = [["A1","A2"], ["B1","B2"], ["C1","C2"]]
new = [x for x in old if x[0] != "A1"]
You can't. You will get an exception. Create a new list as a copy.
>>> disallowed = [1, 2, 3]
>>> my_list = [ [1, 2, 3, 4, 5, 6, 7], [3, 3, 4, 5, 8, 8, 2] ]
>>> filtered_list = [[y for y in x if y not in disallowed] for x in my_list]
>>> print filtered_list
[[4, 5, 6, 7], [4, 5, 8, 8]]
You can actually delete from a list while you iterate over it, provided you to it backwards (so deletion only affects higher indices which you have already seen during this iteration):
data = [["A1","A2"],
["B1","B2"],
["C1","C2"]]
for i, pair in reversed(data):
if pair[0] == 'A1':
del data[i]