统一字典列表
uniqify a list of dictionaries
我在字符串中收到这样的数据:
foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
我希望将其格式化为 table。当数据进入 table 时,我希望每个数据单独一行,除非后 4 个相同(mac、group、vlan、ver)。如果发生这种情况,我希望将数据放在一行上并将两个端口并排打印
Vlan Group Type Version Port List
-----------------------------------------------------------------------
1 239.0.0.4 igmp v1 s2p1, s2p2
1 239.0.0.8 igmp v1 s2p0
我将数据解析成字典列表:
def parse_lines(lines):
headers = lines[0].split()
entries = []
for r in lines[1:]:
if not len(r): continue # skip blank lines
vals = r.split()
e = dict(zip(headers,vals))
entries.append(e)
return entries
def print_table():
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
if foo is not None:
entries = foo.replace("Mac Address", "Mac-Address")
entries = parse_lines(entries.split("\n"))
这给我留下了字典列表,格式示例:
[{'group-addr': '239.0.0.4', 'vlan': '1', 'ver': '1', 'Port': 's2p1', 'Mac-Address': '0100.5e00.0004'}, {'group-addr': '239.0.0.5', 'vlan': '1', 'ver': '1', 'Port': 's2p1', 'Mac-Address': '0100.5e00.0005'}]
我应该如何处理这些以便在打印前比较和存储它们?
创建一个新的字典?比较非端口值与整个先前字典的等价性,然后如果它们都相同,则测试端口并将值添加到新字典?
根据 Peter Bengtsson 的 Fastest way to uniqify a list in Python,结果证明两种性能最好的方法是 将序列转换为集合 - 有序或正常。
那里的代码是相当旧的 Python 版本,因此我不会将其粘贴到此处。相反,Does Python have an ordered set? 给出了第一个选项的概述(第二个是内置类型)。
要成为集合的成员,您的元素必须是可哈希的。所以,你需要例如使用 namedtuple
而不是字典来存储记录。
如果我没理解错的话,你可以按照下面的方法做。
对于您收到的每一行,获取除端口之外的所有值,并将它们作为元组键添加到字典中。
('239.0.0.4','0100.5e00.0004', '1', '1') = (group-addr, Mac-Address, vlan, ver)
当然你可以选择你最喜欢的顺序保存。
与元组键关联的值是集合端口。
最后你会得到很多键值对。全部放入字典。
因此字典将如下所示:
{(group-addr, Mac-Address, vlan, ver): set(port1, port2), ...}
要添加新元素,您可以这样做:
try:
dict[(group-addr, Mac-Address, vlan, ver)].add(port)
except KeyError:
dict[(group-addr, Mac-Address, vlan, ver)] = set(port)
我现在无法测试,但我希望你明白逻辑。
使用Mac Address、group-addr、vlan 和 ver 作为对公共元素进行分组的键,您应该在最初创建 dict 时这样做,但这是一个使用您问题中的数据的示例:
foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
from collections import defaultdict
d = defaultdict(set)
lines = foo.splitlines()
for line in lines[1:]:
prt,mc,gp,vl,vr = line.split()
d[(mc,gp,vl,vr)].add(prt)
print(d)
defaultdict(<type 'set'>, {('0100.5e00.0004', '239.0.0.4', '1', '1'): set(['s2p2', 's2p1']), ('0100.5e00.0005', '239.0.0.8', '1', '1'): set(['s2p0'])})
print "%s %10s %14s %15s" % ("Vlan", "Group", "Version", "Port List")
print "---------------------------------------------------------"
for mc, gp, vl, vr in d:
print("{:<10} {:<14} {:<15}".format(vl,gp,vr)) +",".join(d[mc, gp, vl, v])
Vlan Group Version Port List
---------------------------------------------------------
1 239.0.0.4 1 s2p2,s2p1
1 239.0.0.8 1 s2p0
一个非常幼稚的解决方案:
from collections import defaultdict
def group_entries(entries):
grouped = defaultdict(list)
for entry in entries:
port = entry.pop("Port")
key = tuple(entry.items())
grouped[key].append(port)
results = []
for entry, ports in grouped.items():
entry = dict(entry)
entry["ports"] = ", ".join(ports)
results.append(entry)
return results
def print_table():
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
if foo is not None:
entries = foo.replace("Mac Address", "Mac-Address")
entries = parse_lines(entries.split("\n"))
entries = group_entries(entries)
# etc
但这在更大的数据集上可能效率很低。
重要的是使用您希望保持不变的字段(mac、group、vlan 和 ver)为字典创建一个元组键。然后,创建一个变量来保存端口。我选择了一个列表 - 你可以使用其他人建议的集合 - 当你打印到 "uniqify" 端口时,我将它作为一个选项包括在内。我没有特别对输出进行任何格式化 - 只是按照您的指南进行操作。我看不出最后的 table 中的 "type" 是从哪里来的 - 但我相信你可以适应它。
此外,您的最终 table 不包含 MAC 列。如果您不需要每个 MAC 地址一行,只需将其从字典键
中删除
foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
lines = foo.splitlines()
headers = lines[0]
machineDict={}
for line in lines[1:]:
prt,mac,grp,vl,vr = line.split()
try:
#try to add a new port to the entry with this key
machineDict[(mac,grp,vl,vr)].append(prt)
except KeyError:
#key error signals the dictionary doesn't contain that key
# so create an entry
machineDict[(mac,grp,vl,vr)] = [prt]
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
for (mac,grp,vl,vr),portList in machineDict.items():
print "%s %10s %10s %14s %15s" % (vl,grp,"typeVar",vr,list(set(portList)))
请注意 list(set(portList))
构造只是 "uniqifies" 您的 mac 网络的端口列表。因为它们在您的输入数据中可能是独一无二的 - 如果适合您,您可以将其替换为 portList
我在字符串中收到这样的数据:
foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
我希望将其格式化为 table。当数据进入 table 时,我希望每个数据单独一行,除非后 4 个相同(mac、group、vlan、ver)。如果发生这种情况,我希望将数据放在一行上并将两个端口并排打印
Vlan Group Type Version Port List
-----------------------------------------------------------------------
1 239.0.0.4 igmp v1 s2p1, s2p2
1 239.0.0.8 igmp v1 s2p0
我将数据解析成字典列表:
def parse_lines(lines):
headers = lines[0].split()
entries = []
for r in lines[1:]:
if not len(r): continue # skip blank lines
vals = r.split()
e = dict(zip(headers,vals))
entries.append(e)
return entries
def print_table():
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
if foo is not None:
entries = foo.replace("Mac Address", "Mac-Address")
entries = parse_lines(entries.split("\n"))
这给我留下了字典列表,格式示例:
[{'group-addr': '239.0.0.4', 'vlan': '1', 'ver': '1', 'Port': 's2p1', 'Mac-Address': '0100.5e00.0004'}, {'group-addr': '239.0.0.5', 'vlan': '1', 'ver': '1', 'Port': 's2p1', 'Mac-Address': '0100.5e00.0005'}]
我应该如何处理这些以便在打印前比较和存储它们? 创建一个新的字典?比较非端口值与整个先前字典的等价性,然后如果它们都相同,则测试端口并将值添加到新字典?
根据 Peter Bengtsson 的 Fastest way to uniqify a list in Python,结果证明两种性能最好的方法是 将序列转换为集合 - 有序或正常。
那里的代码是相当旧的 Python 版本,因此我不会将其粘贴到此处。相反,Does Python have an ordered set? 给出了第一个选项的概述(第二个是内置类型)。
要成为集合的成员,您的元素必须是可哈希的。所以,你需要例如使用 namedtuple
而不是字典来存储记录。
如果我没理解错的话,你可以按照下面的方法做。
对于您收到的每一行,获取除端口之外的所有值,并将它们作为元组键添加到字典中。
('239.0.0.4','0100.5e00.0004', '1', '1') = (group-addr, Mac-Address, vlan, ver)
当然你可以选择你最喜欢的顺序保存。
与元组键关联的值是集合端口。
最后你会得到很多键值对。全部放入字典。
因此字典将如下所示:
{(group-addr, Mac-Address, vlan, ver): set(port1, port2), ...}
要添加新元素,您可以这样做:
try:
dict[(group-addr, Mac-Address, vlan, ver)].add(port)
except KeyError:
dict[(group-addr, Mac-Address, vlan, ver)] = set(port)
我现在无法测试,但我希望你明白逻辑。
使用Mac Address、group-addr、vlan 和 ver 作为对公共元素进行分组的键,您应该在最初创建 dict 时这样做,但这是一个使用您问题中的数据的示例:
foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
from collections import defaultdict
d = defaultdict(set)
lines = foo.splitlines()
for line in lines[1:]:
prt,mc,gp,vl,vr = line.split()
d[(mc,gp,vl,vr)].add(prt)
print(d)
defaultdict(<type 'set'>, {('0100.5e00.0004', '239.0.0.4', '1', '1'): set(['s2p2', 's2p1']), ('0100.5e00.0005', '239.0.0.8', '1', '1'): set(['s2p0'])})
print "%s %10s %14s %15s" % ("Vlan", "Group", "Version", "Port List")
print "---------------------------------------------------------"
for mc, gp, vl, vr in d:
print("{:<10} {:<14} {:<15}".format(vl,gp,vr)) +",".join(d[mc, gp, vl, v])
Vlan Group Version Port List
---------------------------------------------------------
1 239.0.0.4 1 s2p2,s2p1
1 239.0.0.8 1 s2p0
一个非常幼稚的解决方案:
from collections import defaultdict
def group_entries(entries):
grouped = defaultdict(list)
for entry in entries:
port = entry.pop("Port")
key = tuple(entry.items())
grouped[key].append(port)
results = []
for entry, ports in grouped.items():
entry = dict(entry)
entry["ports"] = ", ".join(ports)
results.append(entry)
return results
def print_table():
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
if foo is not None:
entries = foo.replace("Mac Address", "Mac-Address")
entries = parse_lines(entries.split("\n"))
entries = group_entries(entries)
# etc
但这在更大的数据集上可能效率很低。
重要的是使用您希望保持不变的字段(mac、group、vlan 和 ver)为字典创建一个元组键。然后,创建一个变量来保存端口。我选择了一个列表 - 你可以使用其他人建议的集合 - 当你打印到 "uniqify" 端口时,我将它作为一个选项包括在内。我没有特别对输出进行任何格式化 - 只是按照您的指南进行操作。我看不出最后的 table 中的 "type" 是从哪里来的 - 但我相信你可以适应它。
此外,您的最终 table 不包含 MAC 列。如果您不需要每个 MAC 地址一行,只需将其从字典键
中删除foo = """Port Mac Address group-addr vlan ver
s2p2 0100.5e00.0004 239.0.0.4 1 1
s2p0 0100.5e00.0005 239.0.0.8 1 1
s2p1 0100.5e00.0004 239.0.0.4 1 1"""
lines = foo.splitlines()
headers = lines[0]
machineDict={}
for line in lines[1:]:
prt,mac,grp,vl,vr = line.split()
try:
#try to add a new port to the entry with this key
machineDict[(mac,grp,vl,vr)].append(prt)
except KeyError:
#key error signals the dictionary doesn't contain that key
# so create an entry
machineDict[(mac,grp,vl,vr)] = [prt]
print "%s %10s %10s %14s %15s" % ("Vlan", "Group", "Type", "Version", "Port List")
print "---------------------------------------------------------"
for (mac,grp,vl,vr),portList in machineDict.items():
print "%s %10s %10s %14s %15s" % (vl,grp,"typeVar",vr,list(set(portList)))
请注意 list(set(portList))
构造只是 "uniqifies" 您的 mac 网络的端口列表。因为它们在您的输入数据中可能是独一无二的 - 如果适合您,您可以将其替换为 portList