我问了一个类似的问题(Remove duplicate value from dictionary without removing key),但我想我已经深入研究了这个问题并且可以更好地解释它,因为不幸的是,没有一个答案能够满足我的需求,但他们确实回答了我的问题。问题
我正在从两个.csv文件构建一个OrderedDict。第一个包含位置代码,第二个是按时间列出的硬件重定位。所有位置代码都是唯一的,所以这是我的字典的关键,我有一个循环使用.csv文件构建具有空值的字典。然后我有另一个循环,将硬件数据添加到正确的位置代码。某些硬件数据采用列表形式,因此不可清除。
我遇到的问题是,当硬件移动到新位置时,我需要将其从以前的位置移除。所以它只在代码末尾的一个地方
我的位置代码是;
>1, 2, 3, 4, 5, 6
我的硬件数据按时间顺序排列,
>7pm, 1, 'item1', 'item2', 'item3'
>8pm, 2, 'item4', 'item5', 'item6'
>9pm, 3, 'item7', '', ''
>10pm, 4, 'item8', '', ''
>11pm, 5, 'item1', 'item2', 'item3'
>12am, 6, 'item7', '', ''
>1am, 3, 'item4', 'item5', 'item6'
如果我在没有任何条件语句的情况下运行整个时间范围的代码,那么我的最终字典就像
>myDict = {'1': ['item1', 'item2', 'item3'], '2': ['item4', 'item5', 'item6'],
>'3': 'item7', '4': 'item8', '5': ['item1', 'item2', 'item3'], '6': 'item7'}
但我需要的东西也是如此;
>my Dict = {'1': '', '2':'', '3': ['item4', 'item5', 'item6'], '4':
>'item8', '5': ['item1', 'item2', 'item3'], '6': 'item7'}
因为项目(值)没有按照添加位置(键)的顺序添加到字典中,所以我在构建字典(添加值)时执行此操作非常重要,因为我无法返回通过并在完成后删除重复项。
我尝试了很多东西并得到了不同的结果,但我的最新成果是
locationCSV = open('location.csv', "r")
hardwareCSV = open('hardware.csv', "r")
locationLines = locationCSV.readlines()
hardwareLines = hardwareCSV.readlines()
finalLoc = OrderedDict()
for line in locationLines:
locationList = line.split(",")
code = locationList[0]
finalLoc[code] = ""
for line in hardwareLines:
hardwareList = line.split(",")
hardwareData = [hardwareList[2],hardwareList[3],hardwareList[4]]
for k, v in finalLoc.iteritems():
if hardwareData in finalLoc.itervalues():
finalLoc[k] = ""
finalLoc[hardwareList[1]] = hardwareData
print finalLoc
这会将所有位置都清空。我已经坚持了几天,所以任何帮助都会受到赞赏。
答案 0 :(得分:0)
您的代码存在许多问题,甚至无法实现这一目标,因此这可能不是您真正的代码。但让我们来看看错误:
csvList = line.split(",")
这将为您提供" 1"
和" 'item1'"
等值,我无法想象您的实际需求。
事实上,你的线条末端有杂散的空白意味着它们甚至不会匹配。例如,第二行中的最后一个字符串是" 'item6' "
,但在最后一行中它是" 'item6'"
,它们不是同一个字符串。
如果您使用csv
库而不是尝试自己动手,这会更容易。如果您只想快速解决问题,可以strip
每个条目:
csvList = [item.strip() for item in line.split(",")]
hardwareData = [csvList[2],csvList[3],csvList[4]]
由于您的某些行只有3列,因此会引发IndexError
。如果您只想获得少于3个短行而不是提高的值,您可以这样做:
hardwareData = csvList[2:5]
for k, v in finalLoc.iteritems():
if hardwareData in finalLoc.itervalues():
对于每一行,您将浏览整个字典,并且对于每个条目,搜索整个字典以查看finalLoc
是否为任何值。因此,如果dict中已有10个项目,那么您将找到已存在100次的每一行。这意味着如果你找到你找到的内容,对于每一行,你将把每一行空白10次。
你可能想要if hardwareData == v
。
finalLoc[key] = ""
您尚未在向我们展示的代码中的任何位置定义key
。如果你之前在某个地方定义了它,它将为每一行的100次中的每一次清空相同的值。否则,这只会引发NameError
。
你可能想要finalLoc[k]
。
如果你保留一个反向字典,将每个值映射到它的键,这整个部分会更简单(也更有效)。
无论如何,将所有这些修复程序放在一起,您的代码就可以运行:
from collections import OrderedDict
hardwareLines = """7pm, 1, 'item1', 'item2', 'item3'
8pm, 2, 'item4', 'item5', 'item6'
9pm, 3, 'item7'
10pm, 4, 'item8'
11pm, 5, 'item1', 'item2', 'item3'
12am, 6, 'item9'
1am, 3, 'item4', 'item5', 'item6'""".splitlines()
finalLoc = OrderedDict()
for line in hardwareLines: ##hardware is the second .csv
csvList = [item.strip() for item in line.split(",")]
hardwareData = csvList[2:5]
for k, v in finalLoc.iteritems():
if hardwareData == v:
finalLoc[k] = ""
finalLoc[csvList[1]] = hardwareData
for k, v in finalLoc.iteritems():
print('{}: {}'.format(k, v))
输出结果为:
1:
2:
3: ["'item4'", "'item5'", "'item6'"]
4: ["'item8'"]
5: ["'item1'", "'item2'", "'item3'"]
6: ["'item9'"]
这是使用csv
模块和逆映射的版本:
from collections import OrderedDict
import csv
hardwareLines = """7pm, 1, 'item1', 'item2', 'item3'
8pm, 2, 'item4', 'item5', 'item6'
9pm, 3, 'item7'
10pm, 4, 'item8'
11pm, 5, 'item1', 'item2', 'item3'
12am, 6, 'item9'
1am, 3, 'item4', 'item5', 'item6'""".splitlines()
finalLoc = OrderedDict()
invmap = {}
for row in csv.reader(map(str.rstrip, hardwareLines),
skipinitialspace=True, quotechar="'"):
hardwareData = tuple(row[2:5])
if hardwareData in invmap:
finalLoc[invmap[hardwareData]] = ""
finalLoc[row[1]] = list(hardwareData)
invmap[hardwareData] = row[1]
for k, v in finalLoc.iteritems():
print('{}: {}'.format(k, v))
我仍然需要在每一行上明确地删除多余的尾随空格,但除此之外,csv
为我处理了所有事情 - 并注意到它还删除了每个值周围的多余引号。
同时,invmap
让我只需一步查找,而不必弄清楚如何遍历项目并找到与当前值匹配的每个键。 (请注意,映射必须是1对1,因为如果您已经遇到过两次该值,则第一个值已经被删除。)
当然即使修复剥离和引用问题,结果仍然并不是你想要的。您想要的输出显然只是将单元素列表展开到元素中。如果你想要,你需要明确地这样做。但你可能不想要那个。实际上,您可能希望使用[]
代替''
作为“空”值。这样,您知道值始终包含0个或更多项的列表,而不必将空字符串视为0值,将任何其他字符串视为1值,将列表视为多个值。因此,当您处理它时,而不是像这样编写代码:
if value == '':
return ''
elif isinstance(value, str):
return process_one_value(value)
else:
return map(process_one_value, value)
......你可以这样做:
return map(process_one_value, value)
答案 1 :(得分:0)
我快速而肮脏的版本,专注于维护订单字典。有一个csv模块来处理读取和解析输入数据。
def removeItem(d, item):
# remove item from d, if present
for k,v in d.items():
if item in v:
v.remove(item)
d[k] = v
d=OrderedDict()
for c in loc_codes: #['1','2',....]
d[c]=[]
for line in hardware.split('\n'): # or read line from file
if line:
items = line.split(', ')
items = [l.strip("'") for l in items]
k = items[1].strip()
v = items[2:]
for item in v:
removeItem(d,item)
d[k] += [item]
print d
结果:
OrderedDict([('1', []), ('2', []), ('3', ['item7', 'item4', 'item5', 'item6']), ('4', ['item8']), ('5', ['item1', 'item2', 'item3']), ('6', ['item9'])])
我从[]
值开始,因此可以轻松地从列表中添加和删除项目。如果这很重要,您可以轻松地将[]
值更改为''
。对于大量数据,removeItem
不如使用集合或其他字典的效率高,但它是一种快速而明显的开始方式。此外,它不依赖于任何其他数据结构。
答案 2 :(得分:-1)
我不会担心这个解析方面。因此,假设您已将数据加载为可用格式,例如:
locations = [1, 2, 3, 4, 5, 6]
hardware = [
('7pm', 1, ['item1', 'item2', 'item3']),
('8pm', 2, ['item4', 'item5', 'item6']),
('9pm', 3, ['item7']),
('10pm', 4, ['item8']),
('11pm', 5, ['item1', 'item2', 'item3']),
('12am', 6, ['item9']),
('1am', 3, ['item4', 'item5', 'item6'])
]
(我强烈建议您将解析代码与数据处理代码分开。如果算法没有与CSV解析代码混合,则可以更轻松地推理算法。)
解决这个问题的关键是在处理数据时保留两个序列,一个映射位置到这些位置的项目列表,一个映射项目到它们的位置。第二张地图是第一张地图的倒数。
拥有这些逆映射将让我们在任一方向上查找信息。如果我们有一个位置,我们可以看到哪些项目,如果我们有一个项目,我们可以得到它的位置。
items_by_location = dict() # The items in each location.
locations_by_item = dict() # The location of each item.
# Start with an empty set for the list of items in each location.
for location in locations:
items_by_location[location] = set()
# Iterate over each item in each hardware line one by one.
for time, location, items in hardware:
for item in items:
old_location = locations_by_item.get(item)
new_location = location
# Remove the item from its old location.
if old_location:
items_by_location[old_location].remove(item)
# Add it to its new location.
items_by_location[new_location].add(item)
locations_by_item[item] = new_location
# Now we can iterate over the list and see where each item ended up.
for location, items in items_by_location.items():
print location, items
输出:
1 set([])
2 set([])
3 set(['item6', 'item7', 'item4', 'item5'])
4 set(['item8'])
5 set(['item2', 'item3', 'item1'])
6 set(['item9'])