假设我有一个列表,其中每个索引都是名称,或者保留了前面名称索引的房间列表。
[["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]
对于上面的列表,但不确定这对我有什么帮助,如果有的话。假设如果列表包含名称,则只有一个名称。
答案 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']]