根据索引列表更改列表元素

Change elements of list based on list of indices

索引列表(称为"indlst"),如[[1,0], [3,1,2]]对应元素[1][0]和[3]给定列表(称为 "lst")的 ][1][2],用于在原始列表中更改它们各自的元素?另请注意,索引指的是嵌套到任意深度的元素。例如,给定

indlst = [[1,0], [3,1,2]]
lst = ["a", ["b","c"], "d", ["e", ["f", "g", "h"]]]
required_output = [lst[1][0],lst[3][1][2]]

输出应对应["b","h"]。我知道我可以通过以下代码片段做到这一点(参见 ):

for i in indlst:
    temp = lst
    for j in i:
        temp = temp[j]
    print(temp)
b
h

但是,我需要更改原始列表中的这些元素。例如,将每个访问的元素更改为 "CHANGED":

changed_lst = ["a", ["CHANGED","c"], "d", ["e", ["f", "g", "CHANGED"]]]

只需检查每个序列中最内层的索引 - 当您到达它时, 更改值,而不是深入序列并仅检索元素:

for indices in indlst:
    base = lst
    for i, index in enumerate(indices):
        if i == len(indices) - 1:
            base[index] = "CHANGED"
            break
        base = base[index]

这种替代形式可能更优雅,尽管它只是一回事:

for indices in indlst:
    base = lst
    for index in indices[:-1]: 
        base = base[index]
    base[indices[-1]] = "CHANGED"

本质上,indlst 中的元素是一个索引列表,用于在 lst 中查找特定元素,因此您可以简单地遍历 indlst 中的元素以查找对lst 中您要更改的项目。

def set_item_by_index_list(index_list, list_to_change, new_val):
    l = list_to_change

    # the last index in index_list is the actual item so we don't need
    # to index into it.
    for idx in index_list[:-1]:
        l = l[idx]

    l[index_list[-1]] = new_val

indlst = [[1,0], [3,1,2]]
lst = ["a", ["b","c"], "d", ["e", ["f", "g", "h"]]]

if __name__ == "__main__":
    print lst
    set_item_by_index_list(indlst[0], lst, "BEER")
    print lst

    set_item_by_index_list(indlst[1], lst, "WHISKEY")
    print lst

以上代码产生:

['a', ['b', 'c'], 'd', ['e', ['f', 'g', 'h']]]
['a', ['b', 'BEER'], 'd', ['e', ['f', 'g', 'h']]]
['a', ['b', 'BEER'], 'd', ['e', ['f', 'WHISKEY', 'h']]]

另一种无需显式循环即可访问最内层列表的方法。

from functools import reduce

indlst = [[1,0], [3,1,2]]
lst = ["a", ["b","c"], "d", ["e", ["f", "g", "h"]]]

for inds in indllst:
  innermost_list = reduce(lambda l, r: l[r], inds[:-1], lst)
  innermost_list[inds[-1]] = 'CHANGED'

我的想法

我的解决方案是从最深的元素开始,然后按如下方式返回:

  • 将最深的元素插入到第二深的元素中
  • 将修改后的第二深元素插入第三深元素
  • 等等

执行此操作的一些代码

def set_from_indices(value, indices, array):
    for i in reversed(range(len(indices))): # Pulls back the deepest element available each loop
        current = array.copy() # Without .copy() any changes to current would also change array.
        for index, indice in enumerate(indices):
            if index == i: # If we are on the deepest element available this loop
                current[indice] = value.copy() if isinstance(value, list) else value # Without the .copy() current[indice] just becomes '...'
                value = current.copy() # Make the 
                break
            current = current[indice]
    return value

indlst = [[0,1], [0,0,1], [0,0,0,1]]
lst = [[[[4,3],2],1],0]
for indices in indlst:
    lst = set_from_indices("CHANGED", indices, lst)