oaklander114
oaklander114

Reputation: 3383

return key under condition

I have a dictionary where values are list of not unique values and associated with various keys.

mydict = {120: ["foo", "bar", "cat"], 125: ["dog", "foo", "bar"]}

I have a set of the values in the dictionary

myset = set(['foo', 'bar', 'cat', 'dog'])

I want to return and print only 1 key for each value in the dictionary and with the condition that this key is the largest number to which the value corresponds in the dictionary. To clarify what I mean, this is the result I would like to achieve:

120: "cat"
125: "dog"
125: 'foo'
125: 'bar'

So every value gets printed only once and only with it's largest corresponding number. I've been able to print each value present in the set together with the key but wonder how to build in the conditional aspect:

result = ''
for i in set:
    result += "%s\t%s" % (i, [key for key in dict if i in dict[key]])

Upvotes: 0

Views: 900

Answers (5)

Martijn Pieters
Martijn Pieters

Reputation: 1123400

The most efficient solution is to first collect the maximum key per unique value; you can filter on your set beforehand or re-use the resulting mapping for other sets.

This is a O(N) (linear time) solution, where N is the total number of values in the dictionary. Pre-filtering brings it down below that as you only consider values in your set. Compare that to Jon's answer which uses sorting; a O(NlogN) algorithm.

Post-selection of the set keys looks like:

max_key = {}
for key, values in mydict.iteritems():
    for value in values:
        if key > max_key.get(value, float('-inf')):
            max_key[value] = key

result = {val: max_key[val] for val in myset}

You can use the max_key mapping to produce results for any set now.

Pre-filtering looks like

max_key = {}
for key, values in mydict.iteritems():
    for value in myset.intersection(values):
        if key > max_key.get(value, float('-inf')):
            max_key[value] = key

result = {key: val for val, key in max_key.iteritems()}

but you'd have to re-run the whole algorithm for each new set of values.

Upvotes: 0

Jon Clements
Jon Clements

Reputation: 142206

You don't need to build an intermediate set, you can build a generator over the dict then sort the items and utilise the fact that the last element of a key/value pair will be the highest key entry, and pass it to a dict constructor, eg:

mydict = {120: ["foo", "bar", "cat"], 125: ["dog", "foo", "bar"]}
result = dict(sorted(((v, k) for k in mydict for v in mydict[k])))
# {'foo': 125, 'bar': 125, 'dog': 125, 'cat': 120}

Then output the result values as appropriate.


If you did really want to filter on some key values, then you can use:

required = {'cat', 'foo'}
result = dict(sorted(((v, k) for k in mydict for v in mydict[k] if v in required)))
# {'foo': 125, 'cat': 120}

Upvotes: 1

BlackMamba
BlackMamba

Reputation: 10252

if __name__ == "__main__":
    mydict = {120: ["foo", "bar", "cat"], 125: ["dog", "foo", "bar"]}

    tempdict = {}
    for key, value in mydict.items():
        for item in value:
            tempdict[item] = key

    myset = set(['foo', 'bar', 'cat', 'dog'])
    for item in myset:
        if item in tempdict.keys():
           print "%d: %s" % (tempdict[item], item)

the output is:

125: foo
125: bar
125: dog
120: cat

Upvotes: 0

cengizkrbck
cengizkrbck

Reputation: 704

More clear and convenient solution, i think.

out = {}

for value in myset:
    out[val] = max([key for key, values in mydict.iteritems() if value in values])

output:

{'bar': 125, 'cat': 120, 'dog': 125, 'foo': 125}

Upvotes: 0

user1907906
user1907906

Reputation:

Try this:

from itertools import chain

mydict = {120: ['foo', 'bar', 'cat'], 125: ['dog', 'foo', 'bar']}
values = set(chain(* [v for k, v in mydict.items()]))
# values == {'bar', 'cat', 'dog', 'foo'}

for v in values:
    m = 0
    for k, vl in mydict.items():
        if v in vl and k > m:
            m = k
    print(v + " " + str(m))

Output:

dog 125
bar 125
cat 120
foo 125

Upvotes: 0

Related Questions