1

假设我有一个列表,其中每个索引要么是一个名称,要么是前一个名称索引保留的房间列表。

[["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]对于上面的列表,但不确定这对我有什么帮助,如果有的话。假设如果一个列表包含名称,它只有一个名称。

4

5 回答 5

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

输出:

['鲍勃','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
于 2012-08-16T08:23:50.530 回答
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
于 2012-08-16T08:06:13.333 回答
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

即使没有要关注的房间列表,这也会打印出名称,例如 [['bob'],['susan']]。

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

于 2012-08-16T08:06:49.887 回答
0

真的,您应该dict为此使用 a 。这假定列表的顺序没有改变(名称总是在前)。

正如其他人建议的那样,您应该重新评估您的数据结构。

>>> 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']]
于 2012-08-16T08:15:19.450 回答
0

减少是你的答案。你的数据是这样的:

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']]
于 2012-08-16T09:06:00.897 回答