Python: Arguments to sorting function - python

I am trying to sort a python list of strings. I know that I can use the method sorted and set the attribute key to be a function that implements the behavior I need to sort the elements of the dictionary on. My problem is that this method needs an argument.
UPDATE:
I want the method to generalize to multiple arguments.
Example:
I want to sort a list of strings based on their priorities in 2 dictionaries . So I need to use those priority dictionaries in sorting the list.
I want something like:
sorted(myList, key=sortingAlgorithm(priorityDictionary1, priorityDictionary2), reverse=True)
This can be done if I set the priority dictionaries as global variables and then I will not need to have it as an argument to the sorting algorithm. But, I want to be able to do it without global variables.
If this is not possible, can you please recommend the pythonic way of doing this.
Thanks in advance.

Try key=priorityDictionary.get
get is a method on a dictionary that takes a key and returns a value, if one is found.
Edit: The above solution applies to a case of a single dict. Here is a generalization where values of priorityDict2 are keys for priorityDict1.
And, as Padriac Cunningham points out, you can use a lambda to sort using nested dicts:
output = sorted(myList, key=lambda element: priorityDict1[priorityDict2[element]])

Use a closure.
def something(foo):
def somethingsomething(bar):
return foo(bar)
return somethingsomething
baz = something(len)
print baz('quux')

Code -
priorities = {'xyz' : 1, 'cde' : 3, 'abc' : 4, 'pqr' : 2}
arr = ['abc', 'cde', 'pqr', 'xyz', 'cde', 'abc']
arr.sort(key = lambda s : priorities[s])
print(arr)
Output -
['xyz', 'pqr', 'cde', 'cde', 'abc', 'abc']

Some slightly more standalone examples:
stringList = ["A1", "B3", "C2", "D4"]
priorityDict = {"A1": 1, "B3": 3, "C2": 2, "D4": 4}
# Reference an accessor function
print sorted(stringList, key=priorityDict.get)
# Provide a lambda function (with "free" closure)
print sorted(stringList, key=lambda x: priorityDict[x])
# Provide a normal function (helper definition needs scope visibility
# on priority dict, but doesn't have to be global - you can define
# functions in the scope of other functions a little like lambdas)
def myKey(x):
return priorityDict[x]
print sorted(stringList, key=myKey)

If you want to pass the value from one dict as the key to the next you need a function or a lambda:
l = ["foo", "Foo", "Bar", "bar", "foobar", "Foobar"]
d1 = {"f": 1, "F": 0, "b": 1, "B": 0}
d2 = {1: 10, 0: 20}
print(sorted(l, key=lambda x: d2[d1[x[0]]]))
If a key may not exist you can still use get:
sorted(l, key=lambda x: d2.get(d1.get(x[0], {}), some_default))
Just make sure some_default makes sense and can be compared to the other values.

def sortingAlgorithm(primaryDictionary):
def __cmp__(a, b):
pass # do something with a, b, and primaryDictionary here
return __cmp__

Related

Is it possible for a key to have multiple names in a dictionary?

I'm not sure if this is even possible but it's worth a shot asking.
I want to be able to access the value from indexing one of the values.
The first thing that came to mind was this but of course, it didn't work.
dict = {['name1', 'name2'] : 'value1'}
print(dict.get('name1))
You can use a tuple (as it's immutable) as a dict key if you need to access it by a pair (or more) of strings (or other immutable values):
>>> d = {}
>>> d[("foo", "bar")] = 6
>>> d[("foo", "baz")] = 8
>>> d
{('foo', 'bar'): 6, ('foo', 'baz'): 8}
>>> d[("foo", "baz")]
8
>>>
This isn't "a key having multiple names", though, it's just a key that happens to be built of multiple strings.
Edit
As discussed in the comments, the end goal is to have multiple keys for each (static) value. That can be succinctly accomplished with an inverted dict first, which is then "flipped" using dict.fromkeys():
def foobar():
pass
def spameggs():
pass
func_to_names = {
foobar: ("foo", "bar", "fb", "foobar"),
spameggs: ("spam", "eggs", "se", "breakfast"),
}
name_to_func = {}
for func, names in func_to_names.items():
name_to_func.update(dict.fromkeys(names, func))
If we tried it you way using:
# Creating a dictionary
myDict = {[1, 2]: 'Names'}
print(myDict)
We get an output of:
TypeError: unhashable type: 'list'
To get around this, we can use this method:
# Creating an empty dictionary
myDict = {}
# Adding list as value
myDict["key1"] = [1, 2]
myDict["key2"] = ["Jim", "Jeff", "Jack"]
print(myDict)

Sort python list of dictionaries by key if key exists

I have a list of dictionaries like this:
[{"foo" : "bar", "myKey" : "one"},
{"foo" : "bar", "myKey" : "two"},
{"foo" : "bar", "yourKey" : "three"}]
I'd like to sort it by a key in the dictionary if it exists.
featured = sorted(filesToWrite, key=lambda k: k["myKey"])
This doesn't work if "myKey" doesn't exist. EDIT: If myKey doesn't exist in the dictionary, I'd like it to appear at the end of the list.
I could loop through the list manually and do it myself but I'm sure there is a pythonic way to accomplish my goal without doing all that.
Check out dict.get:
featured = sorted(filesToWrite, key=lambda k: ("myKey" not in k, k.get("myKey", None)))
Output:
[{'foo': 'bar', 'myKey': 'one'}, {'foo': 'bar', 'myKey': 'two'}, {'yourKey': 'three', 'foo': 'bar'}]
The magic happens in the key:
("myKey" in k, k.get("myKey", None)
Which is a two item tuple, like:
(True, "one")
Where the first element is True/False depending on whether or not the key is missing (True comes after False hence the not), and the second element is the value of said key, if it exists. If not, None. (that argument can be skipped, but I included it to be explicit)
If you're really stuck (like I was, due to the structure of the data I was attempting to sort), you may want to break out of the lambda design, at least for diagnostic purposes. Only when I applied the "nonesorter" design shown here did I receive a meaningful diagnostic from Python that showed me that the thing I was sorting wasn't what I thought it was.
More generally: the "lambda"s you see above are shorthand that can be replaced by a full method definition that you can step through line by line with a debugger.
Also, somewhat misleading to the neophyte is the label key= in Python's sort syntax. This is not actually the name of a key field in the dictionary but rather a pointer to a function which will determine order, analogous to Perl's sort parameter called SUBNAME.
As Python.org's Sorting HOW TO describes it,
The value of the key parameter should be a function that takes a
single argument and returns a key to use for sorting purposes. This
technique is fast because the key function is called exactly once for
each input record.
Finally, note that as others have stated, None was a legal item for comparison in Python 2 whereas with Python 3 attempting to compare to None generates a compiler exception.
>>> L = [{"foo" : "bar", "myKey" : "one"},
... {"foo" : "bar", "myKey" : "two"},
... {"foo" : "bar", "yourKey" : "three"}]
>>>
>>> sorted(L, key=lambda d:("myKey" not in d, d.get("myKey")))
[{'foo': 'bar', 'myKey': 'one'}, {'foo': 'bar', 'myKey': 'two'}, {'yourKey': 'three', 'foo': 'bar'}]
What about
sorted((e for e in case if 'myKey' in e), key=lambda x: x['myKey']) + [e for e in case if 'myKey' not in e]
...?
Assuming the 'case' variable holds the list of dicts you just mentioned. I also assume that if the key is not present, it should be ignored, which seems to make sense.

using a single variable to index into nested dictionaries

Imagine you have a dictionary in python: myDic = {'a':1, 'b':{'c':2, 'd':3}}. You can certainly set a variable to a key value and use it later, such as:
myKey = 'b'
myDic[myKey]
>>> {'c':2, 'd':3}
However, is there a way to somehow set a variable to a value that, when used as a key, will dig into sub dictionaries as well? Is there a way to accomplish the following pseudo-code in python?
myKey = "['b']['c']"
myDic[myKey]
>>> 2
So first it uses 'b' as a key, and whatever is reurned it then uses 'c' as a key on that. Obviously, it would return an error if the value returned from the first lookup is not a dictionary.
No, there is nothing you can put into a variable so that myDict[myKey] will dig into the nested dictionaries.
Here is a function that may work for you as an alternative:
def recursive_get(d, keys):
if len(keys) == 1:
return d[keys[0]]
return recursive_get(d[keys[0]], keys[1:])
Example:
>>> myDic = {'a':1, 'b':{'c':2, 'd':3}}
>>> recursive_get(myDic, ['b', 'c'])
2
No, not with a regular dict. With myDict[key] you can only access values that are actually values of myDict. But if myDict contains other dicts, the values of those nested dicts are not values of myDict.
Depending on what you're doing with the data structure, it may be possible to get what you want by using tuple keys instead of nested dicts. Instead of having myDic = {'b':{'c':2, 'd':3}}, you could have myDic = {('b', 'c'):2, ('b', 'd'): 3}. Then you can access the values with something like myDic['b', 'c']. And you can indeed do:
val = 'b', 'c'
myDic[val]
AFAIK, you cannot. If you think about the way python works, it evaluates inside out, left to right. [] is a shorthand for __getitem__ in this case. Thus you would need to parse the arguments you are passing into __getitem__ (whatever you pass in) and handle that intelligently. If you wanted to have such behavior, you would need to subclass/write your own dict class.
myDict = {'a':1, 'b':{'c':2, 'd':3}}
k = 'b'
myDict.get(k) should give
{'c':2, 'd':3}
and either
d.get(k)['c']
OR
k1 = 'c'
d.get(k).key(k1) should give 2
Pretty old question. There is no builtin function for that.
Compact solution using functools.reduce and operator.getitem:
from functools import reduce
from operator import getitem
d = {'a': {'b': ['banana', 'lemon']}}
p = ['a', 'b', 1]
v = reduce(getitem, p, d)
# 'lemon'

Destructuring-bind dictionary contents

I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like
params = {'a':1,'b':2}
a,b = params.values()
But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
Instead of elaborate lambda functions or dictionary comprehension, may as well use a built in library.
One way to do this with less repetition than Jochen's suggestion is with a helper function. This gives the flexibility to list your variable names in any order and only destructure a subset of what is in the dict:
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Also, instead of joaquin's OrderedDict you could sort the keys and get the values. The only catches are you need to specify your variable names in alphabetical order and destructure everything in the dict:
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
How come nobody posted the simplest approach?
params = {'a':1,'b':2}
a, b = params['a'], params['b']
Python is only able to "destructure" sequences, not dictionaries. So, to write what you want, you will have to map the needed entries to a proper sequence. As of myself, the closest match I could find is the (not very sexy):
a,b = [d[k] for k in ('a','b')]
This works with generators too:
a,b = (d[k] for k in ('a','b'))
Here is a full example:
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
Here's another way to do it similarly to how a destructuring assignment works in JS:
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
What we did was to unpack the params dictionary into key values (using **) (like in Jochen's answer), then we've taken those values in the lambda signature and assigned them according to the key name - and here's a bonus - we also get a dictionary of whatever is not in the lambda's signature so if you had:
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
After the lambda has been applied, the rest variable will now contain:
{'c': 3}
Useful for omitting unneeded keys from a dictionary.
Hope this helps.
Maybe you really want to do something like this?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
If you are afraid of the issues involved in the use of the locals dictionary and you prefer to follow your original strategy, Ordered Dictionaries from python 2.7 and 3.1 collections.OrderedDicts allows you to recover you dictionary items in the order in which they were first inserted
(Ab)using the import system
The from ... import statement lets us desctructure and bind attribute names of an object. Of course, it only works for objects in the sys.modules dictionary, so one could use a hack like this:
import sys, types
mydict = {'a':1,'b':2}
sys.modules["mydict"] = types.SimpleNamespace(**mydict)
from mydict import a, b
A somewhat more serious hack would be to write a context manager to load and unload the module:
with obj_as_module(mydict, "mydict_module"):
from mydict_module import a, b
By pointing the __getattr__ method of the module directly to the __getitem__ method of the dict, the context manager can also avoid using SimpleNamespace(**mydict).
See this answer for an implementation and some extensions of the idea.
One can also temporarily replace the entire sys.modules dict with the dict of interest, and do import a, b without from.
Warning 1: as stated in the docs, this is not guaranteed to work on all Python implementations:
CPython implementation detail: This function relies on Python stack frame support
in the interpreter, which isn’t guaranteed to exist in all implementations
of Python. If running in an implementation without Python stack frame support
this function returns None.
Warning 2: this function does make the code shorter, but it probably contradicts the Python philosophy of being as explicit as you can. Moreover, it doesn't address the issues pointed out by John Christopher Jones in the comments, although you could make a similar function that works with attributes instead of keys. This is just a demonstration that you can do that if you really want to!
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
This solution allows you to pick some of the keys, not all, like in JavaScript. It's also safe for user-provided dictionaries
With Python 3.10, you can do:
d = {"a": 1, "b": 2}
match d:
case {"a": a, "b": b}:
print(f"A is {a} and b is {b}")
but it adds two extra levels of indentation, and you still have to repeat the key names.
Look for other answers as this won't cater to the unexpected order in the dictionary. will update this with a correct version sometime soon.
try this
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]
result:
a == 'Apple'
b == 'Banana'
c == 'Carrot'
Well, if you want these in a class you can always do this:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
Based on #ShawnFumo answer I came up with this:
def destruct(dict): return (t[1] for t in sorted(dict.items()))
d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)
(Notice the order of items in dict)
An old topic, but I found this to be a useful method:
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
locals()[key] = data[key]
This method loops over every key in your dictionary and sets a variable to that name and then assigns the value from the associated key to this new variable.
Testing:
print(a)
print(b)
print(c)
Output
Apple
Banana
Carrot
An easy and simple way to destruct dict in python:
params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2
I don't know whether it's good style, but
locals().update(params)
will do the trick. You then have a, b and whatever was in your params dict available as corresponding local variables.
Since dictionaries are guaranteed to keep their insertion order in Python >= 3.7, that means that it's complete safe and idiomatic to just do this nowadays:
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
Output:
1
2

a(*{'q':'qqq'}),why only print key

def a(*x):
print x
a({'q':'qqq'})
a(*{'q':'qqq'})#why only print key.
traceback:
({'q': 'qqq'},)
('q',)
That's how dictionaries get converted to sequences.
tuple(dictionary) = tuple(dictionary.keys())
for a similar reason
for x in dictionary:
assigns keys, not pairs, to x
When you're calling a function, using an asterisk before a list or dict will pass it in as positional parameters.
For example:
>>> a(*('test', 'testing'))
('test', 'testing')
>>> a(*{'a': 'b', 'c': 'd'})
('a', 'c')
Using * in front of an expression in a function call iterates over the value of the expression (your dict, in this case) and makes each item in the iteration another parameter to the function invocation. Iterating over a dict in Python yields the keys (for better or worse).
Iterating a dictionary will yield its keys.
d = {'a': 1, 'b': 2, 'c': 3 }
for x in d:
print x # prints a, b, c but not necessarily in that order
sorted(d): # Gives a, b, c in that order. No 1/2/3.
If you want to get both keys and values from a dictionary, you can use .items() or .iteritems()
sorted(d.items()) # [('a,' 1), ('b', 2), ('c', 3)]
You are asking for a list of arguments, and then telling python to send a dict as a sequence of arguments. When a dict is converted to a sequence, it uses the keys.
I guess you are really looking for **, not *.
a(*{'q' : 'qqq'})
will try to expand your dict ({'q':'qqq'}) into an itemized list of arguments for the function.
Note that:
tuple({'q' : 'qqq'})
returns ('q',), which is exactly what you're seeing. When you coerce a dictionary to a list/tuple, you only get the list of keys.
Probably because that's what a dictionary returns when you do a standard iteration over it. It gets converted to a sequence containing it's keys. This example exhibits the same behaviour:
>>> for i in {"a": "1", "b": "2"}:
... print i
...
a
b
To get what I assume you expect you would pass it as variable keyword arguments instead, like this:
>>> def a(**kwargs):
... print kwargs
...
>>> a(**{"a": "1", "b": "2"})
{'a': '1', 'b': '2'}
Note that you are now basically back where you began and have gained nothing.

Categories

Resources