Python2: access nested list using a list of indices - python

how do I access an element of a nested list with another list which contains the indices?
e.g:
# this is the variable containing the indices
a = [0, 1]
b = [[1,2],[3,4]]
in reality, these lists are filled with elements of self defined classes and the list containing the "coordinates" (a) has more than 2 elements.
Is there any possibility to access b[0][1] automatically? Previously, I used this code:
c = deepcopy(b)
for el in a:
c = c[el]
but since b is pretty big, I'd love to get rid of that deepcopy without manipulating b in reality.
I am happy about any suggestions :)
Thanks!

Just toss it in a function. That will keep it scoped so you don't overwrite the original value
def nested_getitem(container, idxs):
haystack = container
for idx in idxs:
haystack = haystack[idx]
return haystack
DEMO:
>>> a = [0, 1]
>>> b = [[1, 2], [3, 4]]
>>> nested_getitem(b, a)
2
You could probably do this with a functools.reduce as well, if you were insane.
import functools
import operator
def nested_getitem(container, idxs):
return functools.reduce(operator.getitem, idxs, container)

Related

Lists, how to add multiple values at one time

i just wondered if there is a way to append at one time multiple values i.e.
defining an function with the parameter x, that will automaticly append the amount of elements definied by an x.
def Append(x):
L = []
L.append(x - Elements)
return L
Thanks in advance
Try this to append several similar values to an existing list:
>>> l = [1,2]
>>> l += [3]*2
>>> l
[1, 2, 3, 3]
.extend() list method works too
>>> l = [1,2]
>>> l.extend([3]*2)
>>> l
[1, 2, 3, 3]
i meant it to add an x-Amount of Elements, not an specific one but just for example: Append(20) -> Should add 20 new elements

printing items in a list represented by bit list

I have this problem on writing a python function which takes a bit list as input and prints the items represented by this bit list.
so the question is on Knapsack and it is a relatively simple and straightforward one as I'm new to the python language too.
so technically the items can be named in a list [1,2,3,4] which corresponds to Type 1, Type 2, Type 3 and etc but we won't be needing the "type". the problem is, i represented the solution in a bit list [0,1,1,1] where 0 means not taken and 1 means taken. in another words, item of type 1 is not taken but the rest are taken, as represented in the bit list i wrote.
now we are required to write a python function which takes the bit list as input and prints the item corresponding to it in which in this case i need the function to print out [2,3,4] leaving out the 1 since it is 0 by bit list. any help on this? it is a 2 mark question but i still couldn't figure it out.
def printItems(l):
for x in range(len(l)):
if x == 0:
return False
elif x == 1:
return l
i tried something like that but it is wrong. much appreciated for any help.
You can do this with the zip function that takes two tiers Lee and returns them in pairs:
for bit_item, item in zip(bit_list, item_list):
if bit_item:
print item
Or if you need a list rather than printing them, you can use a list comprehension:
[item for bit_item, item in zip(bit_list, item_list) if bit_item]
You can use itertools.compress for a quick solution:
>>> import itertools
>>> list(itertools.compress(itertools.count(1), [0, 1, 1, 1]))
[2, 3, 4]
The reason your solution doesn't work is because you are using return in your function, where you need to use print, and make sure you are iterating over your list correctly. In this case, enumerate simplifies things, but there are many similar approaches that would work:
>>> def print_items(l):
... for i,b in enumerate(l,1):
... if b:
... print(i)
...
>>> print_items([0,1,1,1])
2
3
4
>>>
You may do it using list comprehension with enumerate() as:
>>> my_list = [0, 1, 1, 1]
>>> taken_list = [i for i, item in enumerate(my_list, 1) if item]
>>> taken_list # by default start with 0 ^
[2, 3, 4]
Alternatively, in case you do not need any in-built function and want to create your own function, you may modify your code as:
def printItems(l):
new_list = []
for x in range(len(l)):
if l[x] == 1:
new_list.append(x+1) # "x+1" because index starts with `0` and you need position
return new_list
Sample run:
>>> printItems([0, 1, 1, 1])
[2, 3, 4]

How to find duplicates in a list without creating a separate list?

How to find the duplicates in a list without creating any other list?
Example
A = [1,2,1,3,4,5,4]
At the end
A = [1,4]
So you want a function which takes a list, A, and mutates that list to contain only those elements which were originally duplicated? I assume the restriction on creating new lists applies to any new collection. It is best to be as clear as possible of the requirements when asking a question about algorithms.
It seems an odd requirement that no other collections be made in this algorithm, but it is possible.
A simple but inefficient solution would be to approach it like this:
for each element, x
set a boolean flag value, for example, hasDuplicates to false
for each element right of x, y
if y is a duplicate of x, remove it and set hasDuplicates to true
if hasDuplicates is false, remove x
If the restriction of not creating another collection can be relaxed, or if the result of the algorithm can be a new list rather than the old one modified, you will find much more (time) efficient ways of doing this.
I would go with checking, for each element, if it appears before it but not after. If it doesn't fit, then either it is not a duplicate or it is an other occurence of the duplicate that you don't want to keep. Either cases we don't keep it.
def simplify(a_list):
for i in range(len(a_list) - 1, -1, -1):
value = a_list[i]
if not value in a_list[:i] or value in a_list[i+1:]:
del a_list[i]
Not sure if using slices fit your requirements though.
Usage:
>>> A = [1,2,1,3,4,5,4]
>>> simplify(A)
>>> A
[1, 4]
>>> A = [1,1,1,1,1,2,2,2,2]
>>> simplify(A)
>>> A
[1, 2]
>>> A = [1,1,1,1,1]
>>> simplify(A)
>>> A
[1]
>>> A = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> simplify(A)
>>> A
[]
You can use set to get only the unique values and then remove them, one-by-one, from the original list - so that only the duplicates will remain:
a = [1,2,1,3,4,5,4]
s = list(set(a))
for x in s:
a.remove(x)
print a # [1, 4]
Another elegant option which I 'stole' from Ritesh Kumar is:
collect only the items that appear more than once, use set to remove the dups, and wrap it with list to return a list as a result:
a = [1,2,1,3,4,5,4]
print list(set([x for x in a if a.count(x) > 1])) # [1, 4]
This should do what you need, barring clarification:
def find_duplicated_items(data):
seen = set()
duplicated = set()
for x in data:
if x in seen:
duplicated.add(x)
else:
seen.add(x)
return duplicated
It takes an iterable and returns a set; you can turn it into a list with list(results).
UPDATE:
Here's another way of doing it, as a generator. Just because :).
from collections import Counter
def find_duplicated(iterable, atleast=2):
counter = Counter()
yielded = set()
for item in iterable:
counter[item] += 1
if (counter[item] >= atleast) and (item not in yielded):
yield item
yielded.add(item)
This code appears to delete 2nd duplicates and non duplicates in place, yielding the old list containing only unique duplicates. I haven't thoroughly tested it. Note that time required will scale as O(N**2) where N is the length of the input list.
Unlike other solutions, there are no new lists constructed here, not even a list for a for loop or list comprehension.
File: "dup.py"
def dups(mylist):
idx = 0
while(idx<len(mylist)):
delidx = idx+1
ndeleted = 0
while delidx < len(mylist):
if mylist[delidx] == mylist[idx]:
del mylist[delidx]
ndeleted += 1
else:
delidx += 1
if ndeleted==0:
del mylist[idx]
else:
idx += 1
return mylist
Usage (iPython)
In [1]: from dup import dups
In [2]: dups([1,1,1,1,1])
Out[2]: [1]
In [3]: dups([1,1,2,1,1])
Out[3]: [1]
In [4]: dups([1,1,2,2,1])
Out[4]: [1, 2]
In [5]: dups([1,1,2,1,2])
Out[5]: [1, 2]
In [6]: dups([1,2,3,1,2])
Out[6]: [1, 2]
In [7]: dups([1,2,1,3,4,5,4])
Out[7]: [1, 4]

Manipulating Python Lists

I have a list of 100 elements. I am trying to create a function that will make 300 copies of that list and then store those copies into a blank list. I then need the function to choose an indexed value at random from each copied list. So, it might choose the 25th index value in the first copied list, and then it might choose the 60th index value in the next copied list. Then, the index of the value is an argument of a pre-defined function. The problem is that my copied lists are not being manipulated.
my code is as follows:
def condition_manipulate(value):
list_set=[] #this is the list in which the copied lists will go
for i in range(0,value):
new_list=initial_conditions[:] #initial_conditions is the list to be copied
list_set.append(new_list)
for i in list_set: #My confusion is here. I need the function to choose
for j in i: #A random value in each copied list that resides
x=random.choice(i) #In list_set and then run a predefined function on it.
variable=new_sum(i.index(x)
i[i.index(x)]=variable
return list_set
#running condition_manipulate(300) should give me a list with 300 copies of a list
#Where a random value in each list is manipulated by the function new_sum
I have tried almost everything. What am I doing wrong? Any Help will be appreciated. Thanks.
Try:
import random
def condition_manipulate(value):
list_set=[]
for i in range(value):
new_list=initial_conditions[:]
i=random.choice(range(len(initial_conditions)))
new_list[i]=new_sum(new_list[i])
list_set.append(new_list)
return list_set
If you really need copies of lists rather than shallow copies then you need to:
import copy
oldlist = [.....]
newlist = copy.deepcopy(oldlist)
Otherwise all the copies are really the same list.>>> o = [1, 2, 3]
>>> n = o
>>> n.append(4)
>>> o
[1, 2, 3, 4]
>>> n = copy.deepcopy(o)
>>> n
[1, 2, 3, 4]
>>> n.append(5)
>>> n
[1, 2, 3, 4, 5]
>>> o
[1, 2, 3, 4]
>>>

Passing a list while retaining the original

So I'm teaching myself Python, and I'm having an issue with lists. I want to pass my function a list and pop items off it while retaining the original list. How do I make python "instance" the passed list rather that passing a pointer to the original one?
Example:
def burninate(b):
c = []
for i in range(3):
c.append(b.pop())
return c
a = range(6)
d = burninate(a)
print a, d
Output: [0, 1, 2] [5, 4, 3]
Desired output: [0, 1, 2, 3, 4, 5] [5, 4, 3]
Thanks!
As other answers have suggested, you can provide your function with a copy of the list.
As an alternative, your function could take a copy of the argument:
def burninate(b):
c = []
b = list(b)
for i in range(3):
c.append(b.pop())
return c
Basically, you need to be clear in your mind (and in your documentation) whether your function will change its arguments. In my opinion, functions that return computed values should not change their arguments, and functions that change their arguments should not return anything. See python's [].sort(), [].extend(), {}.update(), etc. for examples. Obviously there are exceptions (like .pop()).
Also, depending on your particular case, you could rewrite the function to avoid using pop() or other functions that modify the argument. e.g.
def burninante(b):
return b[:-4:-1] # return the last three elements in reverse order
You can call burninate() with a copy of the list like this:
d = burninate(a[:])
or,
d = burninate(list(a))
The other alternative is to make a copy of the list in your method:
def burninate(b):
c=[]
b=b[:]
for i in range(3):
c.append(b.pop())
return c
>>> a = range(6)
>>> b = burninate(a)
>>> print a, b
>>> [0, 1, 2, 3, 4, 5] [5, 4, 3]
A slightly more readable way to do the same thing is:
d = burninate(list(a))
Here, the list() constructor creates a new list based on a.
A more general solution would be to import copy, and use copy.copy() on the parameter.
Other versions:
def burninate(b):
c = []
for i in range(1, 4):
c.append(b[-i])
return c
def burninate(b):
c = b[-4:-1]
c.reverse()
return c
And someday you will love list comprehensions:
def burninate(b):
return [b[-i] for i in range(1,4)]
You can use copy.deepcopy()
burninate = lambda x: x[:-4:-1]

Categories