【问题标题】:Replacing items in a list替换列表中的项目
【发布时间】:2019-12-02 14:14:10
【问题描述】:

我正在尝试根据子字符串匹配替换列表中的项目

我有以下列表

x = ['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']

如果有两个项目,例如D-cat和cat,我想用不带前缀的项目替换带前缀的项目。 即 D-cat 必须替换为 cat。同样,我想替换 所有前缀为xxx的xxx。

我厌倦了使用replace

x = [animal.replace('D-cat','cat') for animal in x] 

预期结果:

x = ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']

我不确定如何为所有项目实现这一点。

我想请教一些建议。

编辑: 测试用例

x =['C-Rab 6-bit', 'Rab 6-bit']

预期输出:

x=['Rab 6-bit', 'Rab 6-bit']

【问题讨论】:

  • 这两个项目总是一个接一个吗?在R-ratS-rat 的情况下,您要添加什么前缀?如果后面也只有rat 怎么办?
  • 我现在看到你也有L-cat 但是忽略它,所以我猜它是连续的?
  • 并发布预期结果

标签: python list replace


【解决方案1】:

如果您确定所需的单词和前缀是连字符分隔的,并且连字符没有出现在前缀或单词中,这可能会起作用:

 lookup_dict = {animal:True for animal in x if '-' not in animal}

 def get_word(animal):
     without_prefix = animal.split('-')[-1]
     return without_prefix if lookup_dict.get(without_prefix) else animal

 x = [get_word(animal) for animal in x]

注意:更通用的方法是使用正则表达式。这种方法虽然很具体,但与使用正则表达式相比效率更高。

【讨论】:

    【解决方案2】:

    你可以用基本的for循环来做到这一点:

    mylist = ['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']
    
    for i in range(len(mylist)):
        for j in range(len(mylist)):
            if mylist[j] in mylist[i]:
                mylist[i] = mylist[j]
    
    print (mylist)
    

    输出:

    ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']
    

    编辑:测试用例

    mylist = ['C-Rab 6-bit', 'Rab 6-bit']
    ouput >> ['Rab 6-bit', 'Rab 6-bit']
    

    【讨论】:

    • 不确定它是否重要,但这与正则表达式方法之间的区别在于,它不检查前缀的外观 - 它会替换 'my-little-rabbit' 或 'jack -rabbit' 或 '-rabbit'。
    • @Stael 这很容易通过将第二个参数“1”传递给split 来解决。正则表达式对于这种类型的字符串操作来说太过分了。
    • @ncica 在原始帖子的编辑中发布的测试用例失败
    【解决方案3】:

    使用简单的列表理解和str.find 函数:

    x = ['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']
    res = [s[s.find('-')+1:]
           if ('-' in s and s[s.find('-')+1:] in x) else s for s in x[:]]
    print(res)
    

    输出:

    ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']
    

    【讨论】:

      【解决方案4】:

      为了避免双重循环,我将传递一次以散列不带前缀的动物,然后替换:

      #Assuming no one letter animal. 
      #The condition allows for animals with '-' 
      #in the name by insisting '-'  not be the second character.
      #('-' in a) would not have allowed '-' in the name.
      animal_set = set(a for a in x if a[1] != '-')
      for i in range(len(x)):
          animal = x[i].split('-',1)[-1]
          if animal in animal_set: x[i]= animal
      

      我认为这比坚持理解要好,而且速度对于长列表很重要(n^2 与 n 复杂性相比)。这包括在原始列表中使用in 运算符。

      我还要说,在您选择的解决方案中绝对没有用正则表达式 - 字符串的常规拆分或索引在这里可以工作。如果你真的想要一个循环的衬里,你可以使用:

      [a.split('-',1)[-1] if a.split('-',1)[-1] in animal_set else a for a in x] 
      

      但现在你必须拆分两次而不是一次,所以我反对。

      还要注意我传递给拆分的“1” - 这将处理额外的破折号,例如 L-complex-animal,将它们拆分为“L”和“complex-animal”。

      【讨论】:

      • 该代码适用于我原始帖子中的输入。但是,'C-Rab 6-bit', 'Rab 6-bit' 这个测试用例失败了。预期结果'Rab 6-bit', 'Rab 6-bit'
      • @Natasha that == 应该是 != 在第一行,我的错。
      • 太棒了!有可能做到这一点吗? ['eta-C-Rab 6-bit', 'C-Rab 6-bit', 'Rab 6-bit'] 预期输出:['Rab 6-bit', 'Rab 6-bit', 'Rab 6-bit']
      • @Natasha 这需要一个新问题。在您最初的问题中,前缀始终是一个字母(S,R,C)。 eta-C 是 4 个字符的字母单词,甚至包括一个破折号。您需要指定如何知道某些内容是前缀还是名称的一部分 - 这会使问题发生太大变化。
      • 如果您最终提出了一个新问题,请务必提供列表的完整示例,说明您如何确定列表中的项目是否为名称(没有前缀),你可以在这里的 cmets 中链接到它,我会看看(还有其他人会停下来)。
      【解决方案5】:

      对于一个班轮的粉丝(不一定推荐):

      x = ['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']
      [re.sub('\w-', '', i) if re.sub('\w-', '', i) in x else i for i in x]
      
      # ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']
      

      【讨论】:

        【解决方案6】:

        您可以使用set 更快地检查您的列表中是否存在无前缀动物。

        x = ["D-cat", "cat", "C-Rabbit", "Rabbit", "R-rat", "S-rat", "L-cat", "C-Rab 6-bit", "Rab 6-bit"]
        x_set = set(x)
        
        processed_animals = []
        for animal in x:
            no_prefix_animal = animal.split("-", 1)[-1]
            if no_prefix_animal in x_set:
                animal = no_prefix_animal
        
            processed_animals.append(animal)
        
        print(processed_animals)
        # ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat', 'Rab 6-bit', 'Rab 6-bit']
        

        【讨论】:

          【解决方案7】:

          itertools.groupby 的一个解决方案:

          x = ['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']
          
          from itertools import groupby
          
          out = []
          s = sorted(enumerate(x), key=lambda k: (k[1].split()[0].split('-', maxsplit=1)[-1], len(k[1])))
          for v, g in groupby(s, lambda k: k[1].split()[0].split('-', maxsplit=1)[-1]):
              l = [*g]
              remove_prefix = not '-' in l[0][-1].split()[0]
              to_replace = l[0][-1]
              out.extend([(i[0],to_replace) if remove_prefix else i for i in l])
          
          print([i[1] for i in sorted(out)])
          

          打印:

          ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']
          

          带有x = ['C-Rab 6-bit', 'Rab 6-bit'] 打印的测试用例:

          ['Rab 6-bit', 'Rab 6-bit']
          

          【讨论】:

            【解决方案8】:

            您可以将enumerate 与列表理解一起使用:

            import re
            def _strip(x):
              return [re.sub('^[A-Z]\-', '', a) if any(a.endswith(c) and not re.findall('^[A-Z]\-', c) for c in x) \
                      else a for i, a in enumerate(x)]
            
            print(_strip(['D-cat', 'cat', 'C-Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'L-cat']))
            print(_strip(['C-Rab 6-bit', 'Rab 6-bit']))
            

            输出:

            ['cat', 'cat', 'Rabbit', 'Rabbit', 'R-rat', 'S-rat', 'cat']
            ['Rab 6-bit', 'Rab 6-bit']
            

            【讨论】:

              猜你喜欢
              • 2012-11-11
              • 2021-03-06
              • 1970-01-01
              • 1970-01-01
              • 2010-09-15
              • 2019-02-21
              • 2021-10-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多