I'm trying to find the most efficient way in python to create a dictionary of 'guids' (point ids in rhino) and retrieve them depending on the value(s) I assign them, change that value(s) and restoring them back in the dictionary. One catch is that with Rhinoceros3d program the points have a random generated ID number which I don't know so I can only call them depending on the value I give them.
are dictionaries the correct way? should the guids be the value instead of the keys?
a very basic example :
arrPts=[]
arrPts = rs.GetPoints() # ---> creates a list of point-ids
ptsDict = {}
for ind, pt in enumerate(arrPts):
ptsDict[pt] = ('A'+str(ind))
for i in ptsDict.values():
if '1' in i :
print ptsDict.keys()
how can I make the above code print the key that has the value '1' , instead of all the keys? and then change the key's value from 1 to e.g. 2 ?
any help also on the general question would be appreciated to know I'm in the right direction.
Thanks
Pav
You can use dict.items().
An example:
In [1]: dic={'a':1,'b':5,'c':1,'d':3,'e':1}
In [2]: for x,y in dic.items():
...: if y==1:
...: print x
...: dic[x]=2
...:
a
c
e
In [3]: dic
Out[3]: {'a': 2, 'b': 5, 'c': 2, 'd': 3, 'e': 2}
dict.items() returns a list of tuples containing keys and value pairs in python 2.x:
In [4]: dic.items()
Out[4]: [('a', 2), ('c', 2), ('b', 5), ('e', 2), ('d', 3)]
and in python 3.x it returns an iterable view instead of list.
I think you want the GUID's to be values, not keys, since it looks like you want to look them up by something you assign. ...but it really depends on your use case.
# list of GUID's / Rhinoceros3d point ids
arrPts = ['D20EA4E1-3957-11d2-A40B-0C5020524153',
'1D2680C9-0E2A-469d-B787-065558BC7D43',
'ED7BA470-8E54-465E-825C-99712043E01C']
# reference each of these by a unique key
ptsDict = dict((i, value) for i, value in enumerate(arrPts))
# now `ptsDict` looks like: {0:'D20EA4E1-3957-11d2-A40B-0C5020524153', ...}
print(ptsDict[1]) # easy to "find" the one you want to print
# basically make both keys: `2`, and `1` point to the same guid
# Note: we've just "lost" the previous guid that the `2` key was pointing to
ptsDict[2] = ptsDict[1]
Edit:
If you were to use a tuple as the key to your dict, it would look something like:
ptsDict = {(loc, dist, attr3, attr4): 'D20EA4E1-3957-11d2-A40B-0C5020524153',
(loc2, dist2, attr3, attr4): '1D2680C9-0E2A-469d-B787-065558BC7D43',
...
}
As you know, tuples are immutable, so you can't change the key to your dict, but you can remove one key and insert another:
oldval = ptsDict.pop((loc2, dist2, attr3, attr4)) # remove old key and get value
ptsDict[(locx, disty, attr3, attr4)] = oldval # insert it back in with a new key
In order to have one key point to multiple values, you'd have to use a list or set to contain the guids:
{(loc, dist, attr3, attr4): ['D20E...', '1D2680...']}
Related
This question already has answers here:
Reverse / invert a dictionary mapping
(32 answers)
Closed 2 years ago.
I'm new to python and struggling with this question - Create a function which works as follows:
d={1:"x",2:"y",3:"x",4:"z"}
invert_d(d)
{"x":(1,3),"y":(2),"z":(4)}
So invert_d gives a new dict with the values of multiple keys as lists.
I have to say, that I never worked with dicts and started python 1 week ago...so I'm a total newb.
I am new to python.... and I'm struggling with this question:
Edit: I read wrong and fixed the Dict. Sorry guys :(
Also we cant import in the exam
You can use the setdefault method to make the dictionary construction simpler. A very basic way to do it is like this:
d={1:"x",2:"y",3:"x",4:"z"}
def invertDict(d):
result = dict()
for k,v in d.items(): result.setdefault(v,[]).append(k)
return result
print(invertDict(d))
{'x': [1, 3], 'y': [2], 'z': [4]}
If you want a one-liner solution, you can use a dictionary comprehension. Here's one that outputs the list of keys as tuples:
def invertDict(d):
return {v:tuple(inv[v]) for inv in [{}] for k,v in d.items() if [inv.setdefault(v,[]).append(k)] }
print(invertDict(d))
{'x': (1, 3), 'y': (2,), 'z': (4,)}
A dict associates exactly one value with a particular key, so your original dict d would see two different values for the key "x" (first 1 and then 3), and the last one would replace previous ones. If you have access to an interactive Python session, this looks like:
$ python
Python 3.9.1 (default, Dec 13 2020, 11:55:53)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {"x": 1, "y": 2, "x": 3, "z": 5}
>>> d
{'x': 3, 'y': 2, 'z': 5}
(The d by itself there just asks Python to use print to display the current value, as if you had called print(d).)
So at this point you have some options. You could just define a dict to contain the eventual values you want: {"x":(1,3),"y":(2,),"z":(5,)}. Or, if you actually have a source of a stream or list of pairs of values that you want to process into a dict using a function like the one you describe, we can do that, too, but we need to use something like a list of tuples as the input, rather than a dict: l = [("x", 1), ("y", 2), ("x", 3), ("z", 5)]. With that new notion of the input, we could write a function that in some way processes each element of that list and compiles those elements into a particular dict. Here's one way that could look (without any error handling):
from functools import reduce
def add_tuple_to_dict(d, t): # here `d` is the dict and `t` is the tuple
if t[0] in d:
d[t[0]].append(t[1])
else:
d[t[0]] = [t[1],]
return d
def invert_d(list_of_tuples):
return reduce(add_tuple_to_dict, list_of_tuples, {})
Edit: after submitting the above, I now realize you do actually want to invert your dict, which could look like this, instead:
from functools import reduce
def add_tuple_to_dict(d, t): # here `d` is the dict and `t` is the tuple
if t[1] in d:
d[t[1]].append(t[0])
else:
d[t[1]] = [t[0],]
return d
def invert_d(d):
return reduce(add_tuple_to_dict, d.items(), {})
You can use collections.defaultdict to initially create your dictionary having keys with lists as values. Then convert it to a dict having keys with tuples as values:
import collections
d = {1: "x", 2: "y", 3: "x", 4: "z"}
def invert(d):
# first as a dict with lists as values
inv = collections.defaultdict(list)
for k, v in d.items():
# note that the previous VALUE is the key of the inverted dict
inv[v].append(k)
# then convert the existing "list of values" to "tuples of values"
inv = {k: tuple(v) for k, v in inv.items()}
return inv
invert(d)
# {'x': (1, 3), 'y': (2,), 'z': (4,)}
What defaultdict does is when a value in the dict is looked up (get or set), if it doesn't exist, it automatically creates a value with the default factory provided. In this case, list.
So each value when looked up is set to an empty list. In the loop, the new values (keys of the old one), are appended to this list. If it didn't exist before, the new value is appended to the empty list. If it did exist (from a previous iteration) and has a value, then a new value is appended to that list.
Since what you want at the end is tuples of values and not lists, I have a dictionary comprehension which uses the same keys and converts the values to tuples.
You can do a reverse look-up
invert_d = lambda d: { v:[k for k in d if d[k] == v] for v in d.values() }
whereat you can read v as value and k as key.
d={1:"x",2:"y",3:"x",4:"z"}
invert_d(d)
{'x': [1, 3], 'y': [2], 'z': [4]}
Edit
Maybe a brief explanation: This is a Lambda Expressions using two List Comprehensions. One as value and an outer one to wrap it up (actually a dictionary comprehensions but works the same).
This is not really performant but totally sufficient for every day usage.
I want to store key-value pairs, but I don't know how to do it.
What I want to achieve is a variable that would store the different value pairs.
What I would want is something like this:
dic = {}
valuepair = (2,3), "cell1"
Each value pair is unique and I would want to be able to do something like this:
dic[(2,3)] = "cell1"
dic["cell1"] = (2,3)
Is there a way to achieve something like that for many different unique value pairs?
If you ask if you can use a tuple as a key - yes, for example:
dic[(2,3)] = "cell1"
print(dic[(2,3)])
would show cell1
or create an inverse dict like this:
inverse_d = {v:k for key, value in d}
Key-Value pair means a key mapped to a value. And what you are doing is right, but if you got the key, you can get value from it. So you need not store value ("cell1"), again as a key, when it is already a value. Sorry, if I don't get your question. Or you can do this too:
x = [("k1","v1"),("k2,"v2")]
d = dict(x)
print(d)
OUTPUT : {"k1":"v1", "k2":"v2"}
You can always do that, but why would you need that is still a question.
valuepairs = [[(2,3), "cell1"], [(4,5), "cell2"]]
dic = {}
for x, y in valuepairs:
dic[x] = y
dic[y] = x
print(dic)
# {(2, 3): 'cell1', 'cell1': (2, 3), (4, 5): 'cell2', 'cell2': (4, 5)}
I have the following dictionary (short version, real data is much larger):
dict = {'C-STD-B&M-SUM:-1': 0, 'C-STD-B&M-SUM:-10': 4.520475, 'H-NSW-BAC-ART:-9': 0.33784000000000003, 'H-NSW-BAC-ART:0': 0, 'H-NSW-BAC-ENG:-59': 0.020309999999999998, 'H-NSW-BAC-ENG:-6': 0,}
I want to divide it into smaller nested dictionaries, depending on a part of the key name.
Expected output would be:
# fixed closing brackets
dict1 = {'C-STD-B&M-SUM: {'-1': 0, '-10': 4.520475}}
dict2 = {'H-NSW-BAC-ART: {'-9': 0.33784000000000003, '0': 0}}
dict3 = {'H-NSW-BAC-ENG: {'-59': 0.020309999999999998, '-6': 0}}
Logic behind is:
dict1: if the part of the key name is 'C-STD-B&M-SUM', add to dict1.
dict2: if the part of the key name is 'H-NSW-BAC-ART', add to dict2.
dict3: if the part of the key name is 'H-NSW-BAC-ENG', add to dict3.
Partial code so far:
def divide_dictionaries(dict):
c_std_bem_sum = {}
for k, v in dict.items():
if k[0:13] == 'C-STD-B&M-SUM':
c_std_bem_sum = k[14:17], v
What I'm trying to do is to create the nested dictionaries that I need and then I'll create the dictionary and add the nested one to it, but I'm not sure if it's a good way to do it.
When I run the code above, the variable c_std_bem_sum becomes a tuple, with only two values that are changed at each iteration. How can I make it be a dictionary, so I can later create another dictionary, and use this one as the value for one of the keys?
One way to approach it would be to do something like
d = {'C-STD-B&M-SUM:-1': 0, 'C-STD-B&M-SUM:-10': 4.520475, 'H-NSW-BAC-ART:-9': 0.33784000000000003, 'H-NSW-BAC-ART:0': 0, 'H-NSW-BAC-ENG:-59': 0.020309999999999998, 'H-NSW-BAC-ENG:-6': 0,}
def divide_dictionaries(somedict):
out = {}
for k,v in somedict.items():
head, tail = k.split(":")
subdict = out.setdefault(head, {})
subdict[tail] = v
return out
which gives
>>> dnew = divide_dictionaries(d)
>>> import pprint
>>> pprint.pprint(dnew)
{'C-STD-B&M-SUM': {'-1': 0, '-10': 4.520475},
'H-NSW-BAC-ART': {'-9': 0.33784000000000003, '0': 0},
'H-NSW-BAC-ENG': {'-59': 0.020309999999999998, '-6': 0}}
A few notes:
(1) We're using nested dictionaries instead of creating separate named dictionaries, which aren't convenient.
(2) We used setdefault, which is a handy way to say "give me the value in the dictionary, but if there isn't one, add this to the dictionary and return it instead.". Saves an if.
(3) We can use .split(":") instead of hardcoding the width, which isn't very robust -- at least assuming that's the delimiter, anyway!
(4) It's a bad idea to use dict, the name of a builtin type, as a variable name.
That's because you're setting your dictionary and overriding it with a tuple:
>>> a = 1, 2
>>> print a
>>> (1,2)
Now for your example:
>>> def divide_dictionaries(dict):
>>> c_std_bem_sum = {}
>>> for k, v in dict.items():
>>> if k[0:13] == 'C-STD-B&M-SUM':
>>> new_key = k[14:17] # sure you don't want [14:], open ended?
>>> c_std_bem_sum[new_key] = v
Basically, this grabs the rest of the key (or 3 characters, as you have it, the [14:None] or [14:] would get the rest of the string) and then uses that as the new key for the dict.
I am creating a dictionary, that potentially has keys that are the same, but the values of each key are different. Here is the example dict:
y = {44:0, 55.4:1, 44:2, 5656:3}
del y[44]
print y
{5656: 3, 55.399999999999999: 1}
I would like to be able to do something like:
del y[44:0]
Or something of that nature.
You never had duplicate keys:
>>> y = {44:0, 55.4:1, 44:2, 5656:3}
>>> y
{5656: 3, 55.4: 1, 44: 2}
A dictionary can be initialised with duplicate keys, but all but one will be ignored.
Just an idea- instead of having scalar values, why not a collection of some kind? Perhaps even a set if they are unique values:
myDict = {44:set([1, 2, 3])}
So to add or remove an object:
myDict[44].add(1)
myDict[44].remove(1)
For adding a new key:
if newKey not in myDict:
myDict[newKey] = set() # new empty set
Your question is moot. In your y declaration, the 44:2 wouldn't go alongside 44:0, it would overwrite it. You'd need to use a different key if you want both values in the dictionary.
Before deleting:--
>>>dic={"a":"A","b":"B","c":"C","d":"D","e":"E"}
>>> del dic['a']
after deleting :-
>>>dic
{'b': 'B','c': 'C','d': 'D','e': 'E'}
I need to sort and search through a dictionary. I know that dictionary cannot be sorted. But all I need to do search through it in a sorted format. The dictionary itself is not needed to be sorted.
There are 2 values. A string, which is a key and associated with the key is an integer value. I need to get a sorted representation based on the integer. I can get that with OrderedDict.
But instead of the whole dictionary I need to print just the top 50 values. And I need to extract some of the keys using RegEx. Say all the keys starting with 'a' and of 5 length.
On a side note can someone tell me how to print in a good format in python? Like:
{'secondly': 2,
'pardon': 6,
'saves': 1,
'knelt': 1}
insdead of a single line. Thank you for your time.
If you want to sort the dictionary based on the integer value you can do the following.
d = {'secondly': 2, 'pardon': 6, 'saves': 1, 'knelt': 1}
a = sorted(d.iteritems(), key=lambda x:x[1], reverse=True)
The a will contain a list of tuples:
[('pardon', 6), ('secondly', 2), ('saves', 1), ('knelt', 1)]
Which you can limit to a top 50 by using a[:50] and then search through the keys, with youre search pattern.
There are a bunch of ways to get a sorted dict, sorted and iteritems()are your friends.
data = {'secondly': 2, 'pardon': 6, 'saves': 1, 'knelt': 1}
The pattern I use most is:
key = sorted(data.iteritems())
print key #[('knelt', 1), ('pardon', 6), ('saves', 1), ('secondly', 2)]
key_desc = sorted(data.iteritems(), reverse=True)
print key_desc #[('secondly', 2), ('saves', 1), ('pardon', 6), ('knelt', 1)]
To sort on the value and not the key you need to override sorted's key function.
value = sorted(data.iteritems(), key=lambda x:x[1])
print value #[('saves', 1), ('knelt', 1), ('secondly', 2), ('pardon', 6)]
value_desc = sorted(data.iteritems(),key=lambda x:x[1], reverse=True)
print value_desc #[('pardon', 6), ('secondly', 2), ('saves', 1), ('knelt', 1)]
For nice formatting check out the pprint module.
If I'm understanding correctly, an OrderedDict isn't really what you want. OrderedDicts remember the order in which keys were added; they don't track the values. You could get what you want using generators to transform the initial data:
import re, operator
thedict = {'secondly':2, 'pardon':6, ....}
pat = re.compile('^a....$') # or whatever
top50 = sorted(((k,v) for (k,v) in thedict.iteritems() if pat.match(k)), reverse=True, key=operator.itemgetter(1))[:50]
As you're using OrderedDict already, you can probably do what you need with a list comprehension. Something like:
[ value for value in d.values()[:50] if re.match('regex', value) ]
Please post your current code if you need something more specific.
For the multi-line pretty print, use pprint with the optional width parameter if needed:
In [1]: import pprint
In [2]: d = {'a': 'a', 'b': 'b' }
In [4]: pprint.pprint(d)
{'a': 'a', 'b': 'b'}
In [6]: pprint.pprint(d,width=20)
{'a': 'a',
'b': 'b'}
There are a few different tools that can help you:
The sorted function takes an iterable and iterates through the elements in order. So you could say something like for key, value in d.iteritems().
The filter function takes an iterable and a function, and returns only those elements for which the function evaluates to True. So, for instance, filter(lambda x: your_condition(x), d.iteritems()) would give you a list of key-value tuples, which you could then sort through as above. (In Python 3, filter returns an iterator, which is even better.)
Generator expressions let you combine all of the above into one. For instance, if you only care about the values, you could write (value for key, value in sorted(d.iteritems()) if condition), which would return an iterator.
you could sort though they keys of the dicionary :
dict = {'secondly': 2,
'pardon': 6,
'saves': 1,
'knelt': 1}
for key in sorted(dict.keys()):
print dict[key]
This will sort your output based on the keys.(in your case the string values alphabetically)