There are a number of problems with your code that prevent you from even getting that far, so this can't possibly be your real code. But let's go through the errors:
csvList = line.split(",")
This is going to give you values like " 1"
and " 'item1'"
, which I can't imagine is what you actually want.
In fact, the fact that your lines have stray whitespace at the end means they won't even match up. For example, the last string in the second line is " 'item6' "
, but in the last line it's " 'item6'"
, which aren't the same string.
This would be much easier if you used the csv
library instead of trying to do it yourself. If you just want a quick hack to solve the problem, you can strip
each entry:
csvList = [item.strip() for item in line.split(",")]
hardwareData = [csvList[2],csvList[3],csvList[4]]
Since some of your lines only have 3 columns, this is going to raise an IndexError
. If you just want to just get fewer than 3 values for short rows instead of raising, you can do:
hardwareData = csvList[2:5]
for k, v in finalLoc.iteritems():
if hardwareData in finalLoc.itervalues():
For each line, you're going through the entire dictionary, and for each entry, searching the entire dictionary to see if finalLoc
is a value anywhere. So, if there are already 10 items in the dict, you're going to find each line that already exists 100 times. Which means that if you blank what you find, for each line, you're going to blank every line 10 times.
You probably wanted if hardwareData == v
here.
finalLoc[key] = ""
You haven't defined key
anywhere in the code you've shown us. If you defined it somewhere earlier, it's going to blank out the same value each of the 100 times for each line. Otherwise, this will just raise a NameError
.
You probably wanted finalLoc[k]
here.
This whole part would be a lot simpler (and more efficient) if you kept an inverse dictionary, mapping each value to its key.
Anyway, putting together all those fixes, your code works:
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))
The output is:
1:
2:
3: ["'item4'", "'item5'", "'item6'"]
4: ["'item8'"]
5: ["'item1'", "'item2'", "'item3'"]
6: ["'item9'"]
Here's a version using the csv
module and an inverse mapping:
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))
I still needed to explicitly strip the excess trailing whitespace on each line, but otherwise, csv
took care of everything for me—and notice that it also removes the excess quotes around each value.
Meanwhile, instead of having to figure out how to walk through the items and find every key that matches the current value, the invmap
lets me just look it up in one step. (Notice that the mapping have to be 1-to-1, because if you'd already encountered the value twice, the first one would already have been removed.)
Of course even fixing the stripping and quoting problems, the results still aren't exactly what you want. Your desired output apparently unwraps single-element lists into just the element. If you want that, you'll need to do that explicitly. But you probably don't want that. In fact, you probably want to use []
instead of ''
as your "empty" value. That way, you know the value is always a list of 0 or more items, instead of having to treat an empty string as 0 values, any other string as 1 value, and a list as multiple values. So, when you process it, instead of writing code like this:
if value == '':
return ''
elif isinstance(value, str):
return process_one_value(value)
else:
return map(process_one_value, value)
… you can just do this:
return map(process_one_value, value)