Python:将列表链接在一起

时间:2012-08-16 07:40:29

标签: python list list-comprehension itertools

假设我有一个列表,其中每个索引都是名称,或者保留了前面名称索引的房间列表。

[["Bob"],["125A, "154B", "643A"],["142C", "192B"], ["653G"], 
["Carol"], ["95H", 123C"], ["David"], ["120G"]]

所以在这种情况下,Bob有房间:125A,154B,643A,152C,192B和653G等等。

如何构建一个将上述内容改为以下格式的函数:

[["Bob", "125A, "154B", "643A", "142C", "192B", "653G"], ["Carol"... 

基本上将[name]与所有[房间预订列表]连接起来,直到[name]的下一个实例。我有一个函数,它接受一个列表,如果列表是名称则返回True,如果它是房间预订列表则返回False,所以我有效:

[True, False, False, False, True, False, True False]对于上面的列表,但不确定这对我有什么帮助,如果有的话。假设如果列表包含名称,则只有一个名称。

5 个答案:

答案 0 :(得分:1)

给出以下方法

def is_name(x):
  return # if x is a name or not

简单而简短的解决方案是使用defaultdict


示例:

from collections import defaultdict

def do_it(source):
  dd = defaultdict(lambda: [])
  for item in sum(source, []): # just use your favourite flattening method here
    if is_name(item):
      name = item
    else:
      dd[name].append(item)
  return [[k]+v for k,v in dd.items()]

for s in do_it(l):
  print s

<强>输出:

  

['Bob','125A','154B','643A','142C','192B','653G']
  ['卡罗尔','95H','123C']   ['大卫','120G']


<强>加成:

这个使用发生器进行懒惰

import itertools 

def do_it(source):
  name, items = None, []
  for item in itertools.chain.from_iterable(source):
    if is_name(item):
      if name: 
        yield [name] + items
        name, items = None, []
      name = item
    else:
      items.append(item)
  yield [name] + items

答案 1 :(得分:0)

我将在前言中表示我非常赞同@uʍopǝpısdn的建议。但是,如果您的设置由于某种原因而无法更改它,那么这似乎有效(尽管它并不漂亮):

# Original list
l = [["Bob"],["125A", "154B", "643A"],["142C", "192B"], ["653G"], ["Carol"], ["95H", "123C"], ["David"], ["120G"]]
# This is the result of your checking function
mapper = [True, False, False, False, True, False, True, False]

# Final list
combined = []

# Generic counters
# Position in arrays
i = 0
# Position in combined list
k = 0

# Loop through the main list until the end.
# We don't use a for loop here because we want to be able to control the
# position of i.
while i < len(l):
  # If the corresponding value is True, start building the list
  if mapper[i]:
    # This is an example of how the code gets messy quickly
    combined.append([l[i][0]])
    i += 1
    # Now that we've hit a name, loop until we hit another, adding the
    # non-name information to the original list
    while i < len(mapper) and not mapper[i]:
      combined[k].append(l[i][0])
      i += 1

    # increment the position in our combined list
    k += 1


print combined

答案 2 :(得分:0)

假设根据列表是否包含名称或房间而获取列表并返回True或False的函数名为containsName()...

def process(items):
  results = []
  name_and_rooms = []
  for item in items:
    if containsName(item):
      if name_and_rooms:
        results.append(name_and_rooms[:])
        name_and_rooms = []
      name_and_rooms.append(item[0])
    else:
      name_and_rooms.extend(item)
  if name_and_rooms:
    results.append(name_and_rooms[:])
  return results

即使没有要关注的房间列表,也会打印出姓名,例如: [[ '鲍勃'],[ '苏珊']]。

此外,这不会合并重复的名称,例如[[ '鲍勃'],[ '123'],[ '鲍勃'],[ '456']]。如果需要,那么您需要将名称转换为临时字典,而将每个房间列表作为值。然后在最后吐出dict的键值。但这本身不会保留名称的顺序。如果你想保留名称的顺序,你可以有另一个包含名称顺序的列表,并在吐出dict中的值时使用它。

答案 3 :(得分:0)

真的,你应该使用dict来做这件事。这假定列表的顺序不会改变(名称始终是第一个)。

正如其他人建议你应该重新评估你的数据结构。

>>> from itertools import chain
>>> li_combo = list(chain.from_iterable(lst))
>>> d = {}
>>> for i in li_combo:
...    if is_name(i):
...       k = i
...    if k not in d:
...       d[k] = []
...    else:
...       d[k].append(i)
... 
>>> final_list = [[k]+d[k] for k in d]
>>> final_list
[['Bob', '125A', '154B', '643A', '142C', '192B', '653G'], ['Carol', '95H', '123C'], ['David', '120G']]

答案 4 :(得分:0)

reduce 是您的答案。您的数据是:

l=[['Bob'], ['125A', '154B', '643A'], ['142C', '192B'], ['653G'], ['Carol'], ['95H', '123C'], ['David'], ['120G']]

你说你已经有了一个函数来确定一个元素是否是一个名字。这是我的一个:

import re
def is_name(s):
  return re.match("[A-z]+$",s) and True or False

然后,使用reduce,它是一个衬垫:

reduce(lambda c, n: is_name(n[0]) and c+[n] or c[:-1]+[c[-1]+n], l, [])

结果是:

[['Bob', '125A', '154B', '643A', '142C', '192B', '653G'], ['Carol', '95H', '123C'], ['David', '120G']]