2013-04-20 68 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)    # [] 
相關問題