I have a a class that gets key and value and add them to a dictionary. I am trying to insert into the dict while keeping the order of the values in an ascending order. I know that OrderedDict remembers the order that keys were first inserted, but wondering what to use if I want to keep the values of the dict sorted as well. Here is an example:
rom collections import OrderedDict
from random import randint
class MyDict():
def __init__(self):
self.d=OrderedDict()
def add(self, key, val):
self.d[key] = val
def show(self):
return self.d
i=1
obj=MyDict()
for _ in range(5):
obj.add(i, randint(1,50))
i+=1
print(obj.show())
OrderedDict([(1, 8), (2, 6), (3, 10), (4, 32), (5, 15)])
However, I am looking to get something like this:
OrderedDict([(2, 6), (1, 8), (3, 10), (5, 15), (4, 32)])
Since it is apparent from your comments that you only need the dict sorted upon output but not during a series of insertions, you don't actually need to incur the overhead of sorting upon each insertion, but rather you can add sorting to methods where the order matters, which can be done by subclassing OrderedDict and overriding all such methods with ones that would sort the items first.
The example below overrides the __repr__ method just so that printing the object would produce your desired output, but you should override all the other relevant methods for completeness:
class MyDict(OrderedDict):
def __sorted(self):
sorted_items = sorted(super().items(), key=lambda t: t[::-1])
self.clear()
self.update(sorted_items)
def __repr__(self):
self.__sorted()
return super().__repr__()
# do the same to methods __str__, __iter__, items, keys, values, popitem, etc.
Demo: https://replit.com/#blhsing/RudeMurkyIntegrationtesting
Since you are looking to sort the dictionary based on the values
>>> from collections import OrderedDict
>>> from random import randint
>>>
>>> d = OrderedDict()
>>>
>>> i=1
>>> for _ in range(5):
... d[i] = randint(1,50)
... i+=1
...
>>>
>>> sorted(d.items(),key=lambda x:(x[1],x[0]))
[(2, 2), (5, 20), (3, 35), (1, 36), (4, 47)]
>>>
key within sorted can be used in this case to sort based on values
Related
how to merge two dictionaries in python 2.7 without changing the order of keys in it. as I have to make a CSV file in the required order. I just want to add B dictionary after A dictionary values.
def Merge(A,B):
m=A.copy()
m.update(B)
return m
I am using this method. I also try with +. but the same result. ** is not working in python 2.7
Dictionaries are unordered containers. So, what you say is not correct. That is, if you simply have two dictionaries, then you have no order.
BUT! You have can order the keys of a dictionary, using the OrderedDict container.
You can consider something like:
>>> from collections import OrderedDict
>>> a = OrderedDict({'a': 1, 'b': 2})
>>> b = OrderedDict({'c': 3, 'd': 4})
>>> c = OrderedDict()
>>>for d in [a, b]:
... for k, v in d.items():
... c.update({k :v})
>>> print(c)
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
# Or
>>> d = OrderedDict()
>>> d.update(a.copy())
>>> d.update(b.copy())
>>> print(d)
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
Or, if you want a function:
from collections import OrderedDict
def merge(a, b):
c = OrderedDict()
c.update(a.copy())
c.update(b.copy())
return c
You can read more about OrderedDict here.
Dictionaries are unordered in Python 2. If you need to preserve the order of keys, you'll have to use something else, like a list of pairs, or an OrderedDict. Here's one approach.
from collections import OrderedDict
def merge(a, b):
return OrderedDict((k, v) for d in [a, b] for k, v in d.items())
Dictionaries is considered as an unordered collection of values mapped to certain keys. First, make an ordered dictionary by using OrderedDict from the collections library. Then using the method update() you can create the new dictionary. Here's an example.
# A Python program to demonstrate working of OrderedDict
from collections import OrderedDict
#Form OrderedDicts
dict1 = OrderedDict({"a": 1, "b": 2})
dict2 = OrderedDict({"c": 3, "d": 4})
print "These are two ordered dictionaries:\n"
print dict1 , dict2
#Using update method in python
def Merge(dict1, dict2):
return(dict1.update(dict2))
#Make a duplicate of the first dictionary
dict3 = dict1
#Call function
Merge(dict3, dict2)
print "Combined dictionary:", dict3
If you are just going to make a csv you can create a list of items from both ensuring that the items from one are after the items of the other. Then use the list of tuples to make the csv.
a = {1:1,2:2}
b = {3:3,4:4}
combined = []
combined.extend(a.items())
combined.extend(b.items())
In [8]: combined
Out[8]: [(1, 1), (2, 2), (3, 3), (4, 4)]
I would like to reformat the following list containing tuples with integers (shared between some tuples) and strings (idiosyncratic to each tuple)
mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'), (9, 'fsfjs'),(10, 'dddd'), (10, '33333'), (12, 'fdsss'), (12, 'fsfjs')]
so that each tuple contains an integer and a concatenated string of all strings belonging to it, like so:
mynewlist = [(8, 'dddd, 33333, fdsss'), (9, 'fsfjs'),(10, 'dddd, 333333'), (12, 'fdsss, fsfjs')
After some deliberation, the most parsimonious solution I've come up with is to simply loop across all tuples and concatenate strings until the integer doesn't match the next one:
mynewlist = []
label = ''
for i in range(len(mylist)-1):
if mylist[i][0] != mylist[i+1][0]:
mynewlist.append(tuple([mylist[i][0], label + mylist[i][1]]))
label = ''
else:
label = label + mylist[i][1] + ','
This works fine. However, I'd like to know if there's a more efficient/Pythonic way of producing the list. I considered using a list comprehension, but this wouldn't allow me to select the strings without going through the whole list many times over; the list comprehension would need to be run for each unique integer, which seems wasteful. I also considered pre-selecting the strings associated with a unique integer through indexing, but this appears quite un-Pythonic to me.
Advice is very appreciated. Thanks!
You could use itertools.groupby() to do the grouping here:
from itertools import groupby
from operator import itemgetter
mynewlist = [
(key, ', '.join([s for num, s in group]))
for key, group in groupby(mylist, itemgetter(0))]
This uses list comprehensions to process each group and extract the strings from the grouped tuples for concatenation. The operator.itemgetter() object tells groupby() to group the input on the first element:
>>> from itertools import groupby
>>> from operator import itemgetter
>>> mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'), (9, 'fsfjs'),(10, 'dddd'), (10, '33333'), (12, 'fdsss'), (12, 'fsfjs')]
>>> [(key, ', '.join([s for num, s in group])) for key, group in groupby(mylist, itemgetter(0))]
[(8, 'dddd, 33333, fdsss'), (9, 'fsfjs'), (10, 'dddd, 33333'), (12, 'fdsss, fsfjs')]
Note that the groupby() iterator groups only consecutive matching elements. That means if your input is not sorted, then tuples with the same initial element are not necessarily going to always be put together either. If your input is not sorted and you need all tuples with the same starting element to be grouped regardless of where they are in the input sequence, use a dictionary to group the elements first:
grouped = {}
for key, string in mylist:
grouped.setdefault(key, []).append(string)
mynewlist = [(key, ', '.join([s for num, s in group])) for key, group in grouped.items()]
A defaultdict would do the trick:
from collections import defaultdict
dct = defaultdict(list)
for k, v in mylist:
dct[k].append(v)
mynewlist = [(k, ','.join(v)) for k, v in dct.iteritems()]
You can do it with a custom dict subclass:
class mydict(dict):
def __setitem__(self, key, val):
self.setdefault(key,[]).append(val)
>>> mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'),
... (9, 'fsfjs'),(10, 'dddd'), (10, '33333'),
... (12, 'fdsss'), (12, 'fsfjs')]
>>> d = mydict()
>>> for key, val in mylist:
... d[key] = val
Now d contains something like
{8: ['dddd', '33333', 'fdsss'], 9: ['fsfjs'], 10: ['dddd', '33333'], 12: ['fdsss', 'fsfjs']}
(to within order of items), and you can easily massage this into the form you want:
result = [(key,', '.join(d[key])) for key, value in d]
I have a python dictionary with similar keys and I want to collect all keys (and values) with the same first part (name or title in this case) into a dict or list in order to find the most common values afterwards. As a side-note: I don't know how many copies of a key (with the same first part) exist. Here are 3, but there could be only 2 or more than 3.
{'name=a=AA': (2, 2), 'name=a_copy=AA': (3, 3), 'name=a_copy2=AA': (3, 2),
'title=b=AA': (1, 2), 'title=b_copy=AA': (3, 3), 'title=b_copy2=AA': (1, 2)}
Is this possible? I though about using key.split("=")[0]
Just loop over the key-values and collect them into a dictionary with lists:
results = {}
for key, value in input_dict.items():
prefix = key.partition('=')[0]
results.setdefault(prefix, []).append((key, value))
This splits of the first part using str.partition(); this is faster for the single-split case. You could use key.split('=', 1)[0] as well, however.
Using defaultdict:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for key in D: # this is the original dictionary
... d[key.split("=")[0]].append(key)
...
>>> d
defaultdict(<class 'list'>, {'title': ['title=b_copy2=AA', 'title=b_copy=AA', 'title=b=AA'], 'name': ['name=a=AA', 'name=a_copy=AA', 'name=a_copy2=AA']})
ِAnother way is to use the itertools.groupby method and group the keys according to first item of split over =:
>>> d
{'name=a=AA': (2, 2), 'name=a_copy2=AA': (3, 2), 'title=b=AA': (1, 2), 'name=a_copy=AA': (3, 3), 'title=b_copy=AA': (3, 3), 'title=b_copy2=AA': (1, 2)}
>>>
>>> dd = {}
>>>
>>> for k,v in groupby(d, key=lambda s:s.split('=')[0]):
if k in dd:
dd[k].extend(list(v))
else:
dd[k] = list(v)
>>> dd
{'name': ['name=a=AA', 'name=a_copy2=AA', 'name=a_copy=AA'], 'title': ['title=b=AA', 'title=b_copy=AA', 'title=b_copy2=AA']}
Should I remove item at the index and add item at the index?
Where should I look at to find the source for OrderedDict class?
From the Python documentation:
If a new entry overwrites an existing entry, the original insertion position is left unchanged. Deleting an entry and reinserting it will move it to the end.
The OrderedDict uses the position of a value if it is already present; if it isn't, just treats it as a new value and adds it at the end.
This is in the documentation. If you need to replace and maintain order, you'll have to do it manually:
od = OrderedDict({i:i for i in range(4)})
# od = OrderedDict([(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)])
# Replace the key and value for key == 0:
d = OrderedDict(('replace','key') if key == 0 else (key, value) for key, value in od.items())
# d = OrderedDict([('replace', 'key'), (1, 1), (2, 2), (3, 3), (4, 4)])
# Single value replaces are done easily:
d[1] = 20 # and so on..
Additionally, at the top of the documentation page you'll see a reference to the file containing, among others, the source for the OrderedDict class. It is in collections.py and, actually, the first class defined.
Besides the answers from ~3 years ago in this question:
Can I do an ordered, default dict in Python?
Have any implementations been adopted into recent versions of Python?
If not, why not?
No, for default options, you will only have the built-in dictionary which is not ordered. If you do want an ordered dictionary, you will have to import the module OrderedDict.
from collections import OrderedDict
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
You do, however, have the option of creating a list of tuples which will maintain its order.
Instead of:
a = {'a': 1, 'b': 2, 'c':3}
you would do it as:
a = [('a', 1), ('b', 2), ('c', 3)]
I'm afraid I'm not on 2.7, so cannot test, but this should reimplement a defaultdict subclassed from OrderedDict:
from collections import OrderedDict
class DefaultOrderedDict(OrderedDict):
def __init__(self, default):
self.default = default
def __getitem__(self, x):
try:
return OrderedDict.__getitem__(self, x)
except KeyError:
self[x] = self.default()
return self[x]