I have a list with nested lists and I want to create a function (modify_list) that gets a tuple and modifies the passed pointer with a passed value argument. The problem is that I'm not sure how to modify a nested value like this programmatically by reference.
Simplified example:
l = [[1, [2,3, [4,5,6]]]]
If I call the function modify_list, these would be how to use it and the expected results:
> l[0][1][2][2]
6
> modify_list((0, 1, 2, 2), 8)
> l
[[1, [2,3, [4,5,8]]]]
> modify_list((0, 1, 1), 14)
> l
[[1, [2,14, [4,5,8]]]]
Thanks
You can determine each sublist by accessing it with the respective index. Use the last index to assign the value:
def set_nested_val(l, indices, val):
for i in indices[:-1]:
l = l[i]
l[indices[-1]] = val
Note that this function operates on an arbitrary list (the first argument), and not only l. If you want to always modify l, use functools.partial:
import functools
l = [[1, [2,3, [4,5,6]]]]
modify_list = functools.partial(set_nested_val, l)
Note that nested lists, and accessing values by indicies, are often a sign of a bad data architecture. Have you considered a dict whose keys are tuples?
def modify_list(indices, new_value):
x = reduce(lambda x, i: x[i], indices[:-1], l)
x[indices[-1]] = new_value
Example:
>>> l = [[1, [2, 3, [4, 5, 6]]]]
>>> modify_list((0, 1, 2, 2), 8)
>>> l
[[1, [2, 3, [4, 5, 8]]]]
This method matches what you are asking for in your question, but it probably makes more sense to pass in the list that you want to mutate instead of always modifying the global variable:
def modify_list(lst, indices, new_value):
x = reduce(lambda x, i: x[i], indices[:-1], lst)
x[indices[-1]] = new_value
>>> l = [[1, [2, 3, [4, 5, 6]]]]
>>> modify_list(l, (0, 1, 2, 2), 8)
>>> l
[[1, [2, 3, [4, 5, 8]]]]
Try this:
def modify_list(lst, pos, val):
item = lst
while len(pos) > 1:
item = item[pos[0]]
pos = pos[1:]
item[pos[0]] = val
Or this:
def modify_list(lst, pos, val):
item = lst
for i in pos[:-1]:
item = item[i]
item[pos[-1]] = val
Or this:
def modify_list(lst, pos, val):
reduce(lambda a, e: a[e], pos[:-1], lst)[pos[-1]] = val
In any case, use it like this:
lst = [[1, [2, 3, [4, 5, 6]]]]
modify_list(lst, (0, 1, 2, 2), 8)
lst
> [[1, [2, 3, [4, 5, 8]]]]
Related
please guys, this function is meant to swap a group of items in a list, like the first k items with the last k items, but I dont know how to get the function to work
def swap(L, k):
L[len(L)-k:], L[:k] = L[:k], L[len(L)-k:]
yet when I run this with doctest, it fails
swap([1, 2, 3, 4, 5, 6],2)
Fix the indexes, return a list from the method, and print the resulting list:
def swap(L, k):
return L[(-k):] + L[k:(-k)] + L[:k]
lst = swap([1, 2, 3, 4, 5, 6], 2)
print(lst)
# [5, 6, 3, 4, 1, 2]
Note that the original list is not modified - what is returned is a different list with elements swapped.
The function works perfectly, but you have no other references to the list you're passing in, so it gets discarded. Simply save the list to a name before passing it in.
By the way, the len(L) is unnecessary.
def swap(L, k):
"""
>>> lst = [1, 2, 3, 4, 5, 6]
>>> swap(lst, 2)
>>> lst
[5, 6, 3, 4, 1, 2]
"""
L[-k:], L[:k] = L[:k], L[-k:]
def swap(L, k):
J = L[:k]
L[:k] = L[-2:]
L[-2:] = J
P = [1,2,3,4,5,6]
swap(P,2)
# P ==[5, 6, 3, 4, 1, 2]
From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
I have one list and I want to print out all elements of it but skip one specific.
a = [1,2,3,4,5,6,7,8,9]
I want to print out:
1,3,4,5,6,7,8,9
(in a column like a regular for-loop)
Is this possible?
Just slice on both sides and concatenate:
def skip_over(lst, i):
return lst[:i] + lst[i + 1:]
skip_over([1, 2, 3, 4, 5], 1) # [1, 3, 4, 5]
If you want to skip over all occurrences of a value, filter with a list comprehension:
def skip_all(lst, v):
return [x for x in lst if x != v]
skip_all([1, 2, 3, 2, 4, 5], 2) # [1, 3, 4, 5]
If you want to skip over the first occurrence of a value, then use index to get its index:
def skip_first(lst, v):
try:
i = lst.index(v)
except ValueError:
# value not in list so return whole thing
return lst
return lst[:i] + lst[i + 1:]
skip_first([1, 2, 3, 2, 4, 5], 2) # [1, 3, 2, 4, 5]
If you specify the element to be skipped by its position (index):
for position, element in enumerate(a):
if position != specified_position:
print(element)
If you specify the element to be skipped by its value:
for element in a:
if element != specified_value:
print(element)
I'm currently working with itertools to create and return a list whose elements are lists that contain the consecutive runs of equal elements of the original list.
import itertools
it = [1, 1, 5, 5, 5, 'test', 'test', 5]
new = len(it)
for a in range(new):
return [list(k) for a, k in itertools.groupby(it)]
For the above example the result is:
[[1, 1], [5, 5, 5], ['test', 'test'], [5]]
Can I achieve this without using itertools?
You can pair adjacent items by zipping the list with itself but with a padding of float('nan') since it can't be equal to any object, and then iterate through the zipped pairs to append items to last sub-list of the output list, and add a new sub-list when the adjacent items are different:
output = []
for a, b in zip([float('nan')] + it, it):
if a != b:
output.append([])
output[-1].append(b)
output becomes:
[[1, 1], [5, 5, 5], ['test', 'test'], [5]]
To be honest a simple for loop could make this work, you don't even have to import itertools.
The simplest way to do this is by using this:
it = [1, 1, 5, 5, 5, 'test', 'test', 5]
result = []
for (i, x) in enumerate(it):
if i < 1 or type(x) != type(it[i - 1]) or x != it[i - 1]:
result.append([x])
else:
result[-1].append(x)
print(result)
Or, in function form:
def type_chunk(it):
result = []
for (i, x) in enumerate(it):
if i < 1 or type(x) != type(it[i - 1]) or x != it[i - 1]:
result.append([x])
else:
result[-1].append(x)
return result
You would then use the function like this:
print(type_chunk([1, 1, 5, 5, 5, 'test', 'test', 5]))
You could even skip the type checking and only look for equal values:
def type_chunk(it):
result = []
for (i, x) in enumerate(it):
if i < 1 or x != it[i - 1]:
result.append([x])
else:
result[-1].append(x)
return result
Good luck.
You could have a look at the function in itertools to see how they are doing it.
Here is one way which shows the logic clearly (can be further reduced):
def i_am_itertool():
it = [1, 1, 5, 5, 5, 'test', 'test', 5]
ret = []
temp = []
last = it[0]
for e in it:
if e == last:
temp.append(e)
else:
ret.append(temp) # Add previous group
temp = [e] # Start next group
last = e
ret.append(temp) # Add final group
return ret
print(i_am_itertool())
Output:
[[1, 1], [5, 5, 5], ['test', 'test'], [5]]
I have a list with repeating values as shown below:
x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
This list is generated from a pattern matching regular expression (not shown here). The list is guaranteed to have repeating values (many, many repeats - hundreds, if not thousands), and is never randomly arranged because that's what the regex is matching each time.
What I want is to track the list indices at which the entries change from the previous value. So for the above list x, I want to obtain a change-tracking list [3, 6] indicating that x[3] and x[6] are different from their previous entries in the list.
I managed to do this, but I was wondering if there was a cleaner way. Here's my code:
x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
flag = []
for index, item in enumerate(x):
if index != 0:
if x[index] != x[index-1]:
flag.append(index)
print flag
Output: [3, 6]
Question: Is there a cleaner way to do what I want, in fewer lines of code?
It can be done using a list comprehension, with a range function
>>> x = [1, 1, 1, 2, 2, 2, 3, 3, 3]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]
>>> x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]
You can do something like this using itertools.izip, itertools.tee and a list-comprehension:
from itertools import izip, tee
it1, it2 = tee(x)
next(it2)
print [i for i, (a, b) in enumerate(izip(it1, it2), 1) if a != b]
# [3, 6]
Another alternative using itertools.groupby on enumerate(x). groupby groups similar items together, so all we need is the index of first item of each group except the first one:
from itertools import groupby
from operator import itemgetter
it = (next(g)[0] for k, g in groupby(enumerate(x), itemgetter(1)))
next(it) # drop the first group
print list(it)
# [3, 6]
If NumPy is an option:
>>> import numpy as np
>>> np.where(np.diff(x) != 0)[0] + 1
array([3, 6])
I'm here to add the obligatory answer that contains a list comprehension.
flag = [i+1 for i, value in enumerate(x[1:]) if (x[i] != value)]
instead multi-indexing that has O(n) complexity you can use an iterator to check for the next element in list :
>>> x =[1, 1, 1, 2, 2, 2, 3, 3, 3]
>>> i_x=iter(x[1:])
>>> [i for i,j in enumerate(x[:-1],1) if j!=next(i_x)]
[3, 6]
itertools.izip_longest is what you are looking for:
from itertools import islice, izip_longest
flag = []
leader, trailer = islice(iter(x), 1), iter(x)
for i, (current, previous) in enumerate(izip_longest(leader, trailer)):
# Skip comparing the last entry to nothing
# If None is a valid value use a different sentinel for izip_longest
if leader is None:
continue
if current != previous:
flag.append(i)