在构建字典时有条件地删除OrderedDict值

时间:2013-08-27 20:20:27

标签: python loops dictionary ordereddictionary

我问了一个类似的问题(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

这会将所有位置都清空。我已经坚持了几天,所以任何帮助都会受到赞赏。

3 个答案:

答案 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'])