Related
I have a dictionary whose key values are kind of like this,
CC-1A
CC-1B
CC-1C
CC-3A
CC-3B
CC-5A
CC-7A
CC-7B
CC-7D
SS-1A
SS-1B
SS-1C
SS-3A
SS-3B
SS-5A
SS-5B
lst = ['CC-1A', 'CC-1B', 'CC-1C', 'CC-3A', 'CC-3B', 'CC-5A', 'CC-7A', 'CC-7B',
'CC-7D', 'SS-1A', 'SS-1B', 'SS-1C', 'SS-3A', 'SS-3B', 'SS-5A', 'SS-5B']
d = dict.fromkeys(lst)
^Not exactly in this order, but in fact they are all randomly placed in the dictionary as key values.
Now, I want to sort them. If I use the built in function to sort the dictionary, it sorts all the key values according to the order given above.
However, I want the dictionary to be first sorted based upon the values after the - sign (i.e. 1A, 1B, 1C etc.) and then based upon the first two characters.
So, for the values given above, following would be my sorted list,
CC-1A
CC-1B
CC-1C
SS-1A
SS-1B
SS-1C
CC-3A
CC-3B
SS-3A
SS-3B
CC-5A
and so on
First, sorting is done based upon the "4th" character in the keys. (that is, 1, 3, etc.)
Then sorting is done based upon the last character (i.e. A, B etc.)
Then sorting is done based upon the first two characters of the keys (i.e. CC, SS etc.)
Is there any way to achieve this?
Your "wanted" and your sorting description deviate.
Your "wanted" can be achieved by
di = {"CC-1A":"value1","CC-1A":"value2","CC-1B":"value3",
"CC-1C":"value4","CC-3A":"value5","CC-3B":"value6",
"CC-5A":"value7","CC-7A":"value8","CC-7B":"value9",
"CC-7D":"value0","SS-1A":"value11","SS-1B":"value12",
"SS-1C":"value13","SS-3A":"value14","SS-3B":"value15",
"SS-5A":"value16","SS-5B":"value17"}
print(*((v,di[v]) for v in sorted(di, key= lambda x: (x[3], x[:2], x[4]) )),
sep="\n")
to get
('CC-1A', 'value2')
('CC-1B', 'value3')
('CC-1C', 'value4')
('SS-1A', 'value11')
('SS-1B', 'value12')
('SS-1C', 'value13')
('CC-3A', 'value5')
('CC-3B', 'value6')
('SS-3A', 'value14')
('SS-3B', 'value15')
('CC-5A', 'value7')
('SS-5A', 'value16')
('SS-5B', 'value17')
('CC-7A', 'value8')
('CC-7B', 'value9')
('CC-7D', 'value0')
which sorts by number (Pos 4 - (1based)), Start (Pos 1+2 (1based)) then letter (Pos 5 (1based))
but that conflicts with
First, sorting is done based upon the "4th" character in the keys.
(that is, 1, 3, etc.)
Then sorting is done based upon the last character (i.e. A, B etc.)
Then sorting is done based upon the first two characters of the keys
(i.e. CC, SS etc.)
One suggestion is to use a nested dictionary, so instead of:
my_dict = {'CC-1A1': 2,
'CC-1A2': 3,
'CC-1B': 1,
'CC-1C': 5,
'SS-1A': 33,
'SS-1B': 23,
'SS-1C': 31,
'CC-3A': 55,
'CC-3B': 222,
}
you would have something like:
my_dict = {'CC': {'1A1': 2, '1A2': 3, '1B': 1, '1C': 5, '3A': 55, '3B': 222},
'SS': {'1A': 33, '1B': 22, '1C': 31}
}
which would allow you to sort first based on the leading number/characters and then by group. (Actually I think you want this concept reversed based on your question).
Then you can create two lists with your sorted keys/values by doing something like:
top_keys = sorted(my_dict)
keys_sorted = []
values_sorted = []
for key in top_keys:
keys_sorted.append([f"{key}-{k}" for k in my_dict[key].keys()])
values_sorted.append([v for v in my_dict[key].values()])
flat_keys = [key for sublist in keys_sorted for key in sublist]
flat_values = [value for sublist in values_sorted for value in sublist]
Otherwise, you'd have to implement a custom sorting algorithm based first the characters after the - and subsequently on the initial characters.
You can write a function to build a sorting key that will make the required decomposition of the key strings and return a tuple to sort by. Then use that function as the key= parameter of the sorted function:
D = {'CC-1A': 0, 'CC-1B': 1, 'CC-1C': 2, 'CC-3A': 3, 'CC-3B': 4,
'CC-5A': 5, 'CC-7A': 6, 'CC-7B': 7, 'CC-7D': 8, 'SS-1A': 9,
'SS-1B': 10, 'SS-1C': 11, 'SS-3A': 12, 'SS-3B': 13, 'SS-5A': 14,
'SS-5B': 15}
def sortKey(s):
L,R = s.split("-",1)
return (R[:-1],L)
D={k:D[k] for k in sorted(D.keys(),key=sortKey)}
print(D)
{'CC-1A': 0,
'CC-1B': 1,
'CC-1C': 2,
'SS-1A': 9,
'SS-1B': 10,
'SS-1C': 11,
'CC-3A': 3,
'CC-3B': 4,
'SS-3A': 12,
'SS-3B': 13,
'CC-5A': 5,
'SS-5A': 14,
'SS-5B': 15,
'CC-7A': 6,
'CC-7B': 7,
'CC-7D': 8}
If you expect the numbers to eventually go beyond 9 and want a numerical order, then right justify the R part in the tuple: e.g. return (R[:-1].rjust(10),L)
You could use a custom function that implements your rule as sorting key:
def get_order(tpl):
s = tpl[0].split('-')
return (s[1][0], s[0], s[1][1])
out = dict(sorted(d.items(), key=get_order))
Output:
{'CC-1A': None, 'CC-1B': None, 'CC-1C': None, 'SS-1A': None, 'SS-1B': None, 'SS-1C': None, 'CC-3A': None, 'CC-3B': None, 'SS-3A': None, 'SS-3B': None, 'CC-5A': None, 'SS-5A': None, 'SS-5B': None, 'CC-7A': None, 'CC-7B': None, 'CC-7D': None}
I have the following list of lists: [[100, 23], [101, 34], [102, 35] ... ]
How can i convert it to a dictionary like that: {100: 23, 101: 34, 102: 35 ... }
Here is what i tried:
myDict = dict(map(reversed, myList))
This code works, but it will give the opposite of what i want in the dictionary: {23: 100, 101:34..}
You can pass a list with this format to dict() and it will convert it for you.
myList = [[100, 23], [101, 34], [102, 35]]
myDict = dict(myList)
print(myDict)
According to docs page for dict()
. . . the positional argument must be an iterable object. Each item in the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value.
and here is an example for the same doc page:
...
>>> d = dict([('two', 2), ('one', 1), ('three', 3)])
returns a dictionary equal to {"one": 1, "two": 2, "three": 3}
L = []
A = {}
for x,y in L:
A[x] = y
I have pairs of 4 lists a and b with integer values such as list_1a = [1,2,3,...] and list_1b = [8,11,15,...]. The idea is that the integer values in list_1a are now represented by the integer values in list_1b, and the same for list_2a and list_2b etc.
Now I have a list of 4 columns final_list which contained integer values corresponding to the a lists. I want to map the values in final_list to the values in the b lists. What is the quickest way to do this in python ?
Is there a quicker way than using lists ?
Edit:
To clarify the question, take the following example:
list_1a = [1,2,3]
list_1b = [8,11,15]
list_2a = [5,6,7,8]
list_2b = [22,26,30,34]
list_3a = [11,12,13,14,18]
list_3b = [18,12,25,28,30]
list_4a = [51,61,72,82]
list_4b = [73,76,72,94]
Note that some of these lists can contain more than a million entries (So maybe memory can be an issue)
The lists do not have the same length
All of the integer values in these lists are unique to their lists, i.e. list_1a + list_1b will never have a repeating integer value.
final_list should look like final_list_b after the mapping occurs
final_list_a = [[1,6,11,51],[3,6,14,72]]
final_list_b = [[8,26,18,73],[15,26,28,72]]
To put things into perspective, this questions is for a database application where these "lists" contain auto-generated key values
I think what you want is a dictionary, which associates keys with values. Unless i'm confused about what you want to do here.
So if I make 4 short example lists.
list_1a = [1,2,3,4]
list_1b = [8,11,15,18]
list_2a = [5,6,7,8]
list_2b = [22,26,30,34]
and make them into a big list of all "a" values and all "b" values.
a_list = list_1a + list_2a
b_list = list_1b + list_2b
I can then use zip to merge the lists into a dictionary
my_dict = dict(zip(a_list, b_list))
print(my_dict)
See:
how to merge 2 list as a key value pair in python
for some other ways to do this last bit.
result:
{1: 8, 2: 11, 3: 15, 4: 18, 5: 22, 6: 26, 7: 30, 8: 34}
Now your "a" list makes up the keys of this dictionary.. while the "b" list make up the values. You can access the values by using the keys. here's some examples.
print(my_dict.keys())
print(my_dict.values())
print(my_dict[5])
gives me:
[1, 2, 3, 4, 5, 6, 7, 8]
[8, 11, 15, 18, 22, 26, 30, 34]
22
Is this what you want?
EDIT: I feel that I should note that while my dictionary has printed in order, dictionaries are actually not ordered like lists. You might want to look into collections.OrderedDict or sorted if this is important to you.
Update:
For what you want to do, maybe consider nested dictionaries. You can make a dictionary whose values are dictionaries, also note that when 1a and 1b don't match in length, zip doesn't care and just excludes 60:
list_1a = [1,2,3,4]
list_1b = [8,11,15,18,60]
list_2a = [5,6,7,8]
list_2b = [22,26,30,34]
a_dict = dict(zip(list_1a, list_2a))
b_dict = dict(zip(list_1b, list_2b))
my_dict = {"a" : a_dict, "b" : b_dict}
print(my_dict)
Result:
{'a': {1: 5, 2: 6, 3: 7, 4: 8}, 'b': {8: 22, 18: 34, 11: 26, 15: 30}}
Now you can access the inner values in a different way:
print(my_dict["a"].keys())
print(my_dict["a"].values())
print(my_dict["a"][4])
Result:
[1, 2, 3, 4]
[5, 6, 7, 8]
8
consider x = [10,10,20,20,20,30]
How do i form another list_x1 which contains only same values example: list_x1 = [10,10]
and list_x2 =[20,20] and list_x3 =[30] ?
You can use counter.
from collections import Counter
x = [10, 10, 20, 20, 20, 30]
my_counter = Counter(x)
d = {'list_x{0}'.format(key): [key] * my_counter[key] for key in my_counter}
>>> d
{'list_x10': [10, 10], 'list_x20': [20, 20, 20], 'list_x30': [30]}
One of the issues with your request is that you would need to pre-assign variables, which aren't initially know. I've used a dictionary as a container to hold them.
For a list, [10] * 3 results in [10, 10, 10]. So, [k] * my_counter multiplies the unique key value by the number of occurrences.
With itertools.groupby
>>> from itertools import groupby
>>> x = [10,10,20,20,20,30]
>>> [list(g) for k, g in groupby(x)]
[[10, 10], [20, 20, 20], [30]]
Perhaps the best way is #Alexander's idea with collections, but I always find it helpful to look at more 'native' python code to see what's going on. So here's a way to do it:
x = [10,10,20,20,20,30]
def foo(iterable):
for val in iterable:
cnt = iterable.count(val)
iterable = list(filter(lambda x: x != val, iterable))
if cnt:
yield [val]*cnt
for _ in foo(x):
print(_)
Note that the complexity factor is going to be fairly high. Certainly not O(n) because you have to:
Iterate through each of the values in our main for val in iterable
Iterate through each of the values every time we call iterable.count
Iterate through each of the values when we filter() them to prevent duplicates.
Using collections.Counter:
>>> def list_gen(in_list, elem):
... count = collections.Counter(in_list)
... return [elem] * count[elem]
...
>>> a
[1, 2, 3, 2, 3]
>>> list_gen(a, 2)
[2, 2]
This isn't exactly what you're looking for, but this code will generate a list of lists separating the values.
x = [10, 10, 20, 20, 20, 30]
uniques = set(x)
output = []
for unique in uniques:
unique_count = x.count(unique)
temp = []
for i in range(0, unique_count):
temp.append(unique)
output.append(temp)
You can then use list comprehensions on output
This question already has answers here:
Sorting list based on values from another list
(20 answers)
Closed 8 years ago.
I would like to sort the values of a dictionary (which are lists) based on one of the lists. For example say I have the dictionary:
data = {'AttrA':[2,4,1,3],'AttrB':[12,43,23,25],'AttrC':['a','d','f','z']}
and I would like to sort this based on the values associated with AttrA, such that:
data = {'AttrA':[1,2,3,4],'AttrB':[23,12,25,43],'AttrC':['f','a','z','d']}
Thank you in advance!
Sort each value in your dictionary based on the data['AttrA'] source list, using sorted() and zip(), all in just 3 lines of code:
base = data['AttrA'] # keep a reference to the original sort order
for key in data:
data[key] = [x for (y,x) in sorted(zip(base, data[key]))]
Demo:
>>> data = {'AttrA': [2, 4, 1, 3], 'AttrB': [12, 43, 23, 25], 'AttrC': ['a', 'd', 'f', 'z']}
>>> base = data['AttrA']
>>> for key in data:
... data[key] = [x for (y,x) in sorted(zip(base, data[key]))]
...
>>> data
{'AttrB': [23, 12, 25, 43], 'AttrC': ['f', 'a', 'z', 'd'], 'AttrA': [1, 2, 3, 4]}
from operator import itemgetter
data = {'AttrA':[2,4,1,3],'AttrB':[12,43,23,25],'AttrC':['a','d','f','z']}
sort = itemgetter(*[i for i, v in sorted(enumerate(data['AttrA']), key=itemgetter(1))])
data = dict((k, list(sort(v))) for k, v in data.items())
Or a shorter but less efficient method of creating sort:
sort = itemgetter(*[data['AttrA'].index(v) for v in sorted(data['AttrA'])])
Result:
>>> data
{'AttrB': [23, 12, 25, 43], 'AttrC': ['f', 'a', 'z', 'd'], 'AttrA': [1, 2, 3, 4]}
This uses operator.itemgetter to create a sorting function that grabs items from a sequence in the order determined by data['AttrA'], and then applying that sorting function to each value in the dictionary.
Untested, but should work (or at least be a good place to start)
data = {'AttrA':[2,4,1,3],'AttrB':[12,43,23,25],'AttrC':['a','d','f','z']}
sortkey = 'AttrA'
sortedval = sorted(data['AttrA'])
valmaps = {i:sortedvals.find(num) for i,num in enumerate(data['AttrA'])}
newData = {k:[v[valmaps[i]] for i,_num in enumerate(v)] for k,v in data.items()}
Hope this helps