【问题标题】:Python - Remove between indexes of two values if it occurs twice in a listPython - 如果在列表中出现两次,则在两个值的索引之间删除
【发布时间】:2015-05-09 08:30:55
【问题描述】:

标题确实令人困惑,所以举个例子:假设我有一个值列表[1,2,3,2,1,4,5,6,7,8]。我想在列表中的两个1s 之间删除,并且通过pythonic 方式它也将最终删除第一个1 并输出[1,4,5,6,7,8]。不幸的是,由于我缺乏pythonic能力,我只能制作一些删除第一组的东西:

a = [1,2,3,2,1,4,5,6,7]
uniques = []
junks = []
for value in a:
    junks.append(value)
    if value not in uniques:
        uniques.append(value)
for value in uniques:
    junks.remove(value)
for value in junks:
    a.remove(value)
    a.remove(value)
a[0] = 1
print(a)
[1,4,5,6,7]

适用于第一次出现两次,不适用于较大列表中的下一次出现。我有一个想法,即在第一次出现的索引和第二次出现的索引之间删除,这将保留第二次,而不是让我做一些愚蠢的事情,比如a[0] = 1,但我真的不知道如何实现它。

【问题讨论】:

    标签: python indexing


    【解决方案1】:

    这会按照你的要求做吗:

    a = [1, 2, 3, 2, 1, 4, 5, 6, 7, 8]
    
    def f(l):
        x = l.copy()
        for i in l:
            if x.count(i) > 1:
                first_index = x.index(i)
                second_index = x.index(i, first_index + 1)
                x = x[:first_index] + x[second_index:]
        return x
    

    所以f(a) 的输出将是[1, 4, 5, 6, 7, 8]f([1, 2, 3, 2, 1, 4, 5, 6, 7, 8, 7, 6, 5, 15, 16]) 的输出将是[1, 4, 5, 15, 16]

    【讨论】:

    • 不,在这种情况下,所需的输出是[1, 4, 5, 6, 7, 8]。如果我们有像a = [1, 2, 3, 2, 1, 4, 5, 6, 7, 8, 7, 6, 5, 15, 16] 这样的更大列表,它也不会起作用,在这种情况下,我也想在5s 之间删除。
    • @Sundrah 那么您想删除所有重复值之间的内容吗?你不想同时保留1s,只保留第一个,对吧?
    • [1, 2, 3, 1, 2, 3] 的期望输出是什么?
    • @Markus Meskanen 是的,就是这样。
    • @Julien Spronck 这样的列表永远不会出现在我正在制作的算法中。
    【解决方案2】:

    如果你想找到独特的元素,你可以使用 set 和 list

    mylist = list(set(mylist))
    

    【讨论】:

    • 这样做不会保留任何边缘值,因此对于[1,2,3,2,1,4,5,6,7],它将返回[4,5,6,7] 而不是[1,4,5,6,7]
    【解决方案3】:
    a = [1, 2, 3, 2, 1, 4, 5, 6, 7, 8, 7, 6, 5, 15, 16]
    dup = [x for x in a if a.count(x) > 1] # list of duplicates
    while dup:
        pos1 = a.index(dup[0])
        pos2 = a.index(dup[0], pos1+1)
        a = a[:pos1]+a[pos2:]
        dup = [x for x in a if a.count(x) > 1]
    print a #[1, 4, 5, 15, 16]
    

    一个更有效的解决方案是

    a = [1, 2, 3, 2, 1, 4, 5, 6, 7, 8, 7, 6, 5, 15, 16]
    pos1 = 0
    while pos1 < len(a):
        if a[pos1] in a[pos1+1:]:
            pos2 = a.index(a[pos1], pos1+1)
            a = a[:pos1]+a[pos2:]
        pos1 += 1
    print a #[1, 4, 5, 15, 16]
    

    【讨论】:

    • @Sundrah 这就是你想要的吗?
    • 是的。他们真的需要做一件事情,你可以将多个答案标记为正确。非常感谢您的宝贵时间。
    • 你真的应该在这里使用 for 循环:for pos1 in len(a): sinc eyou're 不管怎样,你只是在 while 循环中调用 pos1 += 1
    • 我不使用 for 循环的原因是因为我想更改 a 而不创建它的副本
    【解决方案4】:

    (这可能不是最有效的方法,但希望能有所帮助)

    你不能只检查某些东西是否出现了两次,如果有,你有 firstIndex,secondIndex,然后:

    a=[1,2,3,4,5,1,7,8,9]
    b=[]
    
    #do a method to get the first and second index of the repeated number then
    
    for index in range(0, len(a)):
        print index
        if index>firstIndex and index<secondIndex: 
            print "We removed: "+ str(a[index])
    
        else:
            b.append(a[index])         
    
    print b
    

    输出是[1,1,7,8,9],这似乎是您想要的。

    【讨论】:

      【解决方案5】:

      做你需要的工作:

      • 重复值的第一个和最后一个位置
      • 之间的所有索引,删除它们

      有趣的是,你可以简单地告诉 python 这样做:

      # we can use a 'smart' dictionary, that can construct default value:
      from collections import defaultdict
      
      # and 'chain' to flatten lists (ranges)
      from itertools import chain
      
      a = [1, 2, 3, 2, 1, 4, 5, 6, 7]
      
      # build dictionary where each number is key, and value is list of positions:
      index = defaultdict(list)
      for i, item in enumerate(a):
          index[item].append(i)
      
      # let's take first only and last index for non-single values
      edges = ((pos[0], pos[-1]) for pos in index.values() if len(pos) > 1)
      
      # we can use range() to get us all index positions in-between
      # ...use chain.from_iterable to flatten our list
      # ...and make set of it for faster lookup:
      to_remove = set(chain.from_iterable(range(start, end) 
                      for start, end in edges))
      result = [item for i, item in enumerate(a) if i not in to_remove]
      # expected: [1, 4, 5, 6, 7]
      print result
      

      当然你可以缩短它:

      index = defaultdict(list)
      for i, item in enumerate([1, 2, 3, 2, 1, 4, 5, 6, 7]):
          index[item].append(i)
      to_remove = set(chain.from_iterable(range(pos[0], pos[-1]) 
                          for pos in index.values() if len(pos) > 1))
      print [item for i, item in enumerate(a) if i not in to_remove]
      

      这个解决方案具有线性复杂性,应该很快。费用是 字典和集合的额外内存,所以你应该小心巨大的数据集。但是,如果您有大量数据,则使用 lst.index 的其他解决方案无论如何都会窒息,因为它们是 O(n^2) 并且有很多取消引用和函数调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-18
        • 2017-07-13
        • 1970-01-01
        • 2018-05-29
        • 1970-01-01
        • 1970-01-01
        • 2021-03-03
        • 2019-09-19
        相关资源
        最近更新 更多