2013-04-20 48 views
5

我有一个看起来像这样的列表:如何删除列表中满足一定条件的最左/最右元素?

['a', 'b', 'c', '', '', '']

这是解析“脏” csv文件的结果。我现在想摆脱右边的空栏。我不能只使用计数,因为长度是可变的。我也不能只用简单的过滤,因为也有看起来像这样的行:

['a1', '', 'c1', '', '']

所以我要保护那些在最右侧的空列。有没有一种惯用的方式来做到这一点?我希望能有一些像“removeWhile”函数那样的功能,我可以在反向列表中应用。

我想出迄今最好的是以下几点:

def filterRow(row): 
    row.reverse() 
    blanks = 0 
    for x in row: 
     if x == '': 
      blanks += 1 
     else: 
      break 
    row.reverse() 
    return row[0:-blanks] 

回答

9
def filterRow(row): 
    while row[-1] == "": 
     row.pop() 

如果你不想做就地出于某种原因,像这样做,而不是:

def filterRow(row): 
    row = list(row) 
    while row[-1] == "": 
     row.pop() 
    return row 

突然离开一个列表的末尾是非常快的,虽然可能稍微快一点来计算最后一个索引并进行分片,但它也会导致更长,更复杂,更难以阅读的代码。因此,现在就选择可读的版本,并且只有在确定它是实践中的重大瓶颈之后再考虑更改它。

为了更加直观的功能,为什么不把它rstrip,而不是filterRow,因为它几乎是str.rstrip确实为字符串的同样的事情?

+0

+1,非常优雅。 [我的回答(http://stackoverflow.com/a/16120252/1600898)演示了切片的方法,但该指数的计算结束了漫长而艰难的理解,或两者兼而有之。 – user4815162342 2013-04-20 12:30:37

+1

+1,但请注意,当所有列都为空时,这会中断。 – root 2013-04-20 13:11:24

+1

@root这是平凡的改变条件'固定,而第一行和行[-1] ==“”',但它是一种耻辱,这种混乱的一流解决方案 - 至少我的预感是,OP的行是从不全是空的。 – user4815162342 2013-04-20 13:43:27

2

也许这样的事情?

>>> l = ['a', 'b', 'c', '', '', ''] 
# iterate through the list in reverse... 
>>> for v in l[::-1]: 
     # when we encounter an element that's not empty, exit the loop 
...  if v: 
...   break 
     # otherwise pop the last element off the end of the list 
...  l.pop() 

>>> l 
['a', 'b', 'c'] 
3

虽然@Lauritz V. Thaulow对你的问题有最清晰的认识,我想你可能会问错误的问题。相反,您应该在阅读csv时去掉空列,而不是在将其转换为列表之后。然后一个简单的line.rstrip(', \n')应该做的。

In [1]: lst = ['a1', '', 'c1', '', ''] 

In [2]: def remove_while(lst): 
    ...:  return ','.join(lst).rstrip(', ').split(',') 

In [3]: remove_while(['a1', '', 'c1', '', '']) 
Out[3]: ['a1', '', 'c1'] 

所以你可以只:

with open('test.csv') as f: 
    for line in f: 
     print line.rstrip(', \n').split(',') 
#['a1', '', 'c1'] 
#['a', 'b', 'c'] 
+0

此解决方案的问题在于它假定固定的CSV语法。尽管有这个名字,CSV支持许多不同的字段分隔符(Excel,至少在欧洲版本中,默认为';',因为','是大多数欧洲语言中的小数分隔符)。然后是引用,它可以创建不同的方式来表示一个空列。除非您自己生成输入CSV,否则应将其处理留给CSV阅读器。 – user4815162342 2013-04-20 13:48:25

+0

@ user4815162342 - 这只是一个玩具的例子。无论如何,你仍然可以使用'csv.reader'和一个生成器,例如:'csv.reader(line.rstrip(',\ n')for f)' - 你仍然必须指定分隔符,但通常你在阅读文件之前知道格式。 – root 2013-04-20 14:37:46

+0

但正是这一点 - 你经常*不知道你会遇到的CSV方言,'csv'模块为你自动检测。 – user4815162342 2013-04-20 17:32:07

1

这样的事情,没有创造任何新的字符串,列表或使用逆转:

In [138]: def remove_while(lis): 
    .....:  le=len(lis) 
    .....:  ind=0 
    .....:  for i in xrange(le-1,-1,-1): 
    .....:   if lis[i]!="": 
    .....:    break 
    .....:   else: 
    .....:    ind+=1 
    .....:  del lis[-ind:] 
    .....:  return lis 
    .....: 

In [139]: remove_while(['a', 'b', 'c', '', '', '']) 
Out[139]: ['a', 'b', 'c'] 

In [140]: remove_while(['a1', '', 'c1', '', '']) 
Out[140]: ['a1', '', 'c1'] 

In [141]: remove_while(['', '', '', '', '']) 
Out[141]: [] 
2

下面是使用一个单一的一个简洁的实现切片:

def filterRow(row): 
    rightmost = next(i for i in reversed(xrange(len(row))) if row[i]) 
    del row[rightmost + 1:] 
    # or, non-destructively: return row[:rightmost + 1] 

说明:

  • reversed(xrange(len(row))以相反顺序产生列表索引;与xrange(len(row) - 1,-1,-1)相同,但更具可读性。

  • i for i in INDICES if row[i]是发电机表达式从右到左,跳过空的索引上迭代。

  • next(iterable)得到所生成的表达的第一个元素。应用于上面的生成器表达式,它返回最右边的非空元素的索引。

  • del row[rightmost + 1:]删除在该行的末尾的所有空元素。 (或者,return row[:rightmost + 1]返回所有元素,直到并包括最右边的非空单)

0

晚,但希望这是在这里:

def strip(xs, predicate=lambda x: not x): 
    """Given a sequence, remove leading/trailing items that match the predicate.""" 
    m = [bool(predicate(x)) for x in xs] 
    try: 
     a = m.index(False) 
     b = m[::-1].index(False) 
     return xs[a:len(xs)-b] 
    except ValueError: 
     return [] 


print strip(['','',1,2,'',3,4,0,None,'',''])  # [1, 2, '', 3, 4] 
print strip([1,2,10,20,3,30,5,6], lambda x: x < 10) # [10, 20, 3, 30] 
print strip([10,20,3,30], lambda x: x < 10)   # [10, 20, 3, 30] 
print strip([1,2,3], lambda x: x < 10)    # [] 
相关问题