python noob here. So I'm making a program that will take a JSON file from a url and parse the information and put it into a database. I have the JSON working, thankfully, but now I am stuck, I'll explain it through my code.
playerDict = {
"zenyatta" : 0,
"mei" : 0,
"tracer" : 0,
"soldier76" : 0,
"ana" : 0,
...}
So this is my original dictionary with the which I then fill with the players data for each hero.
topHeroes = sorted(playerDict.items(),key = operator.itemgetter(1),reverse = True)
I then sort this list and it turns the heroes with the most amount of hours played first.
topHeroesDict = topHeroes[0:3]
playerDict['tophero'] = topHeroesDict[0]
I then get the top three heroes. The second line here prints out a list like so:
'secondhero': ('mercy', 6.0)
Whereas I want the output to be:
'secondhero': 'mercy'
Would appreciate any help i have tried the code below with and without list.
list(topHeroes.keys())[0]
So thanks in advance and apologies for the amount of code!
You could take an approach with enumerate, if instead of "firsthero" you are ok with "Top 1" and so on. With enumerate you can iterate over the list and keep track of the current index, which is used to name the key in this dictionary comprehension. j[0] is the name of the hero, which is the first element of the tuple.
topHeroes = sorted(playerDict.items(),key = operator.itemgetter(1),reverse = True)
topHeroesDict = {"Top "+str(i): j[0] for i, j in enumerate(topHeroes[0:3])}
Alternatively, you could use a dictionary which maps the index to first like this:
topHeroes = sorted(playerDict.items(),key = operator.itemgetter(1),reverse = True)
top = {0: "first", 1: "second", 2: "third"}
topHeroesDict = {top[i]+"hero": j[0] for i, j in enumerate(topHeroes[0:3])}
You do not need any imports to achieve this. Without itemgetter, you can do it in one line like this:
top = {0: "first", 1: "second", 2: "third"}
topHeroesDict = {top[i]+"hero": j[0] for i, j in enumerate(sorted([(i, playerDict[i]) for i in playerDict.keys()], key = lambda x: x[1], reverse = True)[0:3])}
You're sorting an iterable of tuples returned by the items method of the dict, so each item in the sorted list is a tuple containing the hero and their score.
You can avoid using sorted and dict.items altogether and get the leading heroes (without their score) by simply using collections.Counter and then getting the most_common 3 heroes.
from collections import Counter
player_dict = Counter(playerDict)
leading_heroes = [hero for hero, _ in player_dict.most_common(3)]
Related
Basically, I am trying to append values from a list as key-value pairs to a new dictionary. I want the key names in a specific format and order.
#!/usr/bin/env python3
ipv4_list = ["192.168.1.2", "192.168.1.3", "192.168.1.4"]
ipv4_dic = {}
ipv4_len = len(ipv4_list)
i = 1
for val in range(len(ipv4_list)):
ipv4_dic[i] = ipv4_list[val]
i+=1
print(ipv4_dic)
Current output:
{1: '192.168.1.2', 2: '192.168.1.3', 3: '192.168.1.4'}
The above is good but I want to change key names to something like IP1, IP2, etc.
How do I make that in the line ipv4_dic[i] = ipv4_list[key]
I tried something like ipv4_dic["IP"+i] but does not work.
ipv4_dic["IP"+i] = ipv4_list[val]
TypeError: can only concatenate str (not "int") to str
The expected dictionary output as follows:
{IP1: '192.168.1.2', IP2: '192.168.1.3', IP3: '192.168.1.4'}
It does not work because you are trying to concatenate a string with an integer, which is not allowed in Python.
In order to have your code working with the minimum possible amendments is to transform the integer i to a string, via the str() function, i.e. replacing in your code
ipv4_dic["IP"+i] = ipv4_list[val]
with
ipv4_dic["IP"+str(i)] = ipv4_list[val]
However, you can use dict comprehension, i.e. you loop each position of the ipv4_dic you're about to create, placing there a key built from each element of your ipv4_list, in a single line of code.
ipv4_list = ["192.168.1.2", "192.168.1.3", "192.168.1.4"]
ipv4_dic = {f"IP{i+1}": ipv4_list[i] for i in range(len(ipv4_list))}
Explanation:
for each i in the range from 0 to len(ipv4_list) we are building the single dictionary key through an f-string, concatenating the string "IP" to the value i+1 (the range starts at 0, but you want your first dict element to be "IP1", that's why i+1). Then, we set the value for this key as ipv4_list[i], which are the single elements contained in your starting list.
The basic usage is
l = ['A', 'B', 'C']
d = {i : l[i] for i in range(len(l))}
which is pretty similar to your case, without the refinements in the keys creation, since you're incrementing the index (i.e. i) and concatenating it to a string.
Use a dictionary comprehension with enumerate starting a 1:
ipv4_list = ["192.168.1.2", "192.168.1.3", "192.168.1.4"]
ipv4_dic = {f'IP{n}':ip for n,ip in enumerate(ipv4_list,1)}
print(ipv4_dic)
{'IP1': '192.168.1.2', 'IP2': '192.168.1.3', 'IP3': '192.168.1.4'}
ipv4_list = ["192.168.1.2", "192.168.1.3", "192.168.1.4"]
ipv4_dict= {}
# enumerate will give you the index and value of the iterator
# f-string can compute the value inside the {} and format it like a string
for index,value in enumerate(ipv4_list):
ipv4_dict[f"IP{index+1}"] = value
# or
ipv4_dict_1 = {f"IP{index+1}": value for index,value in enumerate(ipv4_list)}
# out:
print(ipv4_dict)
# {'IP1': '192.168.1.2', 'IP2': '192.168.1.3', 'IP3': '192.168.1.4'}
print(ipv4_dict_1)
# {'IP1': '192.168.1.2', 'IP2': '192.168.1.3', 'IP3': '192.168.1.4'}
I almost didn’t post this because Georgio almost had what I would have written: use a dictionary comprehension based on a for loop. Andd tell you about the str and int concatenation.
The only thing missing was using enumerate to get the list indices. Full of goodness, is enumerate. It returns the elements from an iterable as a list of pairs (tuples): first item is the zero-based index, second is what the for loop (well actually the iterable at that index) would normally return.
Then those pairs get assigned to i,v via tuple unpacking.
di = { f"IP{i+1}":v for i,v in enumerate( [" 192.168.1.2", "192.168.1.3", "192.168.1.4"])}
print(di)
See https://docs.python.org/3/library/functions.html#enumerate
Dictionaries work like this:
thisdict = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
Try this:
ipv4_list = ["192.168.1.2", "192.168.1.3", "192.168.1.4"]
ipv4_dic = {}
ipv4_len = len(ipv4_list)
i = 1
for key in range(len(ipv4_list)):
auxStr = "IP" + str(i) #convert i to string, and merging IP
ipv4_dic[auxStr] = ipv4_list[key]
i+=1
print(ipv4_dic)
This should do it!
Happy coding!!!
I am looking for a way to count occurency in a 2D List. For example I have a list like that:
[[John, 3],[Chris, 3],[Bryan,5],[John,3],[John,7]]
As an output I want to count which numbers does John have most common like that
Most common number for the John is: 3
I did it for all the names easily with
Counter(my_list[1]).most_common(5)
Does anyone have a suggestion to do it?
This should work.
from collections import Counter
main_list = [['John', 3],['Chris', 3],['Bryan',5],['John',3],['John',7]] #your_list
new_list = [i[1] for i in main_list if i[0]=='John']
print(Counter(new_list).most_common(1)[0][0])
I would probably re-shape the input data before doing queries on it. Maybe name vs values:
name_lookup = defaultdict(list)
for name, value in my_list:
name_lookup[name].append(value)
name = 'John'
most_common, _ = Counter(name_lookup[name]).most_common(1)[0]
print(f"Most common number for {name} is: {most_common}")
You could also do filtering and mapping:
my_list = [['John', 3],['Chris', 3],['Bryan',5],['John',3],['John',7]]
print(Counter(map(lambda y: y[1], filter(lambda x: x[0] == "John", my_list))).most_common(1))
If you are 100% sure that there always will be exactly 1 most-frequent element for each name, you migth sort by name, groupby by name and then use statistics.mode following way:
import itertools
import statistics
some_data = [['John', 3],['Chris', 3],['Bryan',5],['John',3],['John',7]]
sorted_data = sorted(some_data,key=lambda x:x[0]) # sort by name
most_frequent = {name:statistics.mode(j[-1] for j in list(data)) for name,data in itertools.groupby(sorted_data,key=lambda x:x[0])}
print(most_frequent) # {'Bryan': 5, 'Chris': 3, 'John': 3}
itertools.groupby returns pairs of name-data, but as datas itself contain both key (name in our case) and value (number in our case) we need comprehension to get "raw" values.
def __init__(self, devices, queue):
'''
'''
self.devices = devices
self.queue = queue
values = {k:0 for k in devices.keys()}
values[0xbeef] = len(values) # the number of devices
super(CallbackDataBlock, self).__init__(values)
Can someone help me explain the following two lines:
values = {k:0 for k in devices.keys()}
What does k:0 do?
values[0xbeef] = len(values) # the number of devices
Does this mean that new item {0xbeef: length} is appended in the dict?
The k is the field in the dictionary. The set of all fields is stored in the device.keys() which is most probably a list, we loop through the list, take names of fields and initialize them by zero.
Yes, you are right. The next statement is responsible for adding a new field and initializing it to the length of the array.
{k:0 for k in devices.keys()} creates a dictionary with all keys and 0 for all values. And your assessment is correct, it creates a new key with {value of 0xbeef : number of keys in the dictionary}
in python documentation you can see List Comprehensions
this pattern is important :
expression for item in list if conditional else
or for simple usage :
expression for item in list
in list data type we can use :
list = [0,1,2,3,4,5]
a = [x for x in list]
print (a)
printed :
[1,2,3,4,5]
and we have :
a = [x*2 for x in list]
print (a)
printed :
[2,4,6,8,10]
and for dictionary
in dictionary we have this syntax:
{key1:value1, key2:value2, . . .}
and example :
list = [0,1,2,3,4,5]
d = [k:0 for k in list]
print (d)
in example k:0 maens : put 0 for value of each k
printed :
{1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
one more thing :
python dictionary have to Helpful method:dict.keys(),dict.values()
when we use dict.keys, python return a list of dict's keys
d = {"name":"sam", "job":"programer", "salary":"25000"}
print(d.keys())
print(d.values())
printed :
['name','job','salary']
['sam','programer','25000']
for add a new object in a dictionary we use :
d[newkey]= newValue
for example :
d[10] = 'mary'
print(d[10])
printed :
'mary'
now your answer :
in your code
1) k:0 maens : put 0 for value of each k
2) 0xbeef is a hex code == 48879 in decimal
values[48879] = len(values)
its fill by length of list.
I have a list of strings that looks like that
name=['Jack','Sam','Terry','Sam','Henry',.......]
I want to create a newlist with the logic shown below. I want to go to every entry in name and assign it a number if the entry is seen for the first time. If it is being repeated(as in the case with 'Sam') I want to assign it the corresponding number, include it in my newlist and continue.
newlist = []
name[1] = 'Jack'
Jack = 1
newlist = ['Jack']
name[2] = 'Sam'
Sam = 2
newlist = ['Jack','Sam']
name[3] = 'Terry'
Terry = 3
newlist = ['Jack','Sam','Terry']
name[4] = 'Sam'
Sam = 2
newlist = ['Jack','Sam','Terry','Sam']
name[5] = 'Henry'
Henry = 5
newlist = ['Jack','Sam','Terry','Sam','Henry']
I know this can be done with something like
u,index = np.unique(name,return_inverse=True)
but for me it is important to loop through the individual entries of the list name and keep the logic above. Can someone help me with this?
Try using a dict and checking if keys are already paired to a value:
name = ['Jack','Sam','Terry','Sam','Henry']
vals = {}
i = 0
for entry in name:
if entry not in vals:
vals[entry] = i + 1
i += 1
print vals
Result:
{'Henry': 5, 'Jack': 1, 'Sam': 2, 'Terry': 3}
Elements can be accessed by "index" (read: key) just like you would do for a list, except the "index" is whatever the key is; in this case, the keys are names.
>>> vals['Henry']
5
EDIT: If order is important, you can enter the items into the dict using the number as the key: in this way, you will know which owner is which based on their number:
name = ['Jack','Sam','Terry','Sam','Henry']
vals = {}
i = 0
for entry in name:
#Check if entry is a repeat
if entry not in name[0:i]:
vals[i + 1] = entry
i += 1
print (vals)
print (vals[5])
This code uses the order in which they appear as the key. To make sure we don't overwrite or create duplicates, it checks if the current name has appeared before in the list (anywhere from 0 up to i, the current index in the name list).
In this way, it is still in the "sorted order" which you want. Instead of accessing items by the name of the owner you simply index by their number. This will give you the order you desire from your example.
Result:
>>> vals
{1: 'Jack', 2: 'Sam', 3: 'Terry', 5: 'Henry'}
>>> vals[5]
'Henry'
If you really want to create variable.By using globals() I am creating global variable .If you want you can create local variable using locals()
Usage of globals()/locals() create a dictionary which is the look up table of the variable and their values by adding key and value you are creating a variable
lists1 = ['Jack','Sam','Terry','Sam','Henry']
var = globals()
for i,n in enumerate(nl,1):
if n not in var:
var[n] = [i]
print var
{'Jack':1,'Sam': 2,'Terry': 3, 'Henry':5}
print Jack
1
If order of the original list is key, may I suggest two data structures, a dictionary and a newlist
d = {}
newlist = []
for i,n in enumerate(nl):
if n not in d:
d[n] = [i+1]
newlist.append({n: d[n]})
newlist will return
[{'Jack': [1]}, {'Sam': [2]}, {'Terry': [3]}, {'Sam': [2]}, {'Henry': [5]}]
to walk it:
for names in newlist:
for k, v in names.iteritems():
print('{} is number {}'.format(k, v))
NOTE: This does not make it easy to lookup the number based on the name as other suggested above. That would require more data structure logic. This does however let you keep the original list order, but keep track of the time the name was first found essentially.
Edit: Since order is important to you. Use orderedDict() from the collections module.
Use a dictionary. Iterate over your list with a for loop and then check to see if you have the name in the dictionary with a if statement. enumerate gives you the index number of your name, but keep in mind that index number start from 0 so in accordance to your question we append 1 to the index number giving it the illusion that we begin indexing from 1
import collections
nl = ['Jack','Sam','Terry','Sam','Henry']
d = collections.OrderedDict()
for i,n in enumerate(nl):
if n not in d:
d[n] = [i+1]
print d
Output:
OrderedDict([('Jack', [1]), ('Sam', [2]), ('Terry', [3]), ('Henry', [5])])
EDIT:
The ordered dict is still a dictionary. So you can use .items() to get the key value pairs as tuples. the number is essectially a list so you can do this:
for i in d.items():
print '{} = {}'.format(i[0],i[1][0]) #where `i[1]` gives you the number at that position in the tuple, then the `[0]` gives you the first element of the list.
Output:
Jack = 1
Sam = 2
Terry = 3
Henry = 5
Suppose I have a list where each index is either a name, or a list of rooms the preceding name index reserved.
[["Bob"],["125A, "154B", "643A"],["142C", "192B"], ["653G"],
["Carol"], ["95H", 123C"], ["David"], ["120G"]]
So in this case, Bob has the rooms: 125A, 154B, 643A, 152C, 192B, and 653G reserved, etc.
How do I construct a function which would make the above into the following format:
[["Bob", "125A, "154B", "643A", "142C", "192B", "653G"], ["Carol"...
Essentially concatenating [name] with all the [list of room reservations], until the next instance of [name]. I have a function which takes a list, and returns True if a list is a name, and False if it is a list of room reservations, so effectively I have:
[True, False, False, False, True, False, True False] for the above list, but not sure how that would help me, if at all. Assume that if a list contains names, it only has one name.
Given the following method
def is_name(x):
return # if x is a name or not
a simply and short solution is to use a defaultdict
Example:
from collections import defaultdict
def do_it(source):
dd = defaultdict(lambda: [])
for item in sum(source, []): # just use your favourite flattening method here
if is_name(item):
name = item
else:
dd[name].append(item)
return [[k]+v for k,v in dd.items()]
for s in do_it(l):
print s
Output:
['Bob', '125A', '154B', '643A', '142C', '192B', '653G']
['Carol', '95H', '123C']
['David', '120G']
Bonus:
This one uses a generator for laziness
import itertools
def do_it(source):
name, items = None, []
for item in itertools.chain.from_iterable(source):
if is_name(item):
if name:
yield [name] + items
name, items = None, []
name = item
else:
items.append(item)
yield [name] + items
I'll preface this by saying that I strongly agree with #uʍopǝpısdn's suggestion. However if your setup precludes changing it for some reason, this seems to work (although it isn't pretty):
# Original list
l = [["Bob"],["125A", "154B", "643A"],["142C", "192B"], ["653G"], ["Carol"], ["95H", "123C"], ["David"], ["120G"]]
# This is the result of your checking function
mapper = [True, False, False, False, True, False, True, False]
# Final list
combined = []
# Generic counters
# Position in arrays
i = 0
# Position in combined list
k = 0
# Loop through the main list until the end.
# We don't use a for loop here because we want to be able to control the
# position of i.
while i < len(l):
# If the corresponding value is True, start building the list
if mapper[i]:
# This is an example of how the code gets messy quickly
combined.append([l[i][0]])
i += 1
# Now that we've hit a name, loop until we hit another, adding the
# non-name information to the original list
while i < len(mapper) and not mapper[i]:
combined[k].append(l[i][0])
i += 1
# increment the position in our combined list
k += 1
print combined
Assume that the function which takes a list and returns True or False based on whether list contains name or rooms is called containsName() ...
def process(items):
results = []
name_and_rooms = []
for item in items:
if containsName(item):
if name_and_rooms:
results.append(name_and_rooms[:])
name_and_rooms = []
name_and_rooms.append(item[0])
else:
name_and_rooms.extend(item)
if name_and_rooms:
results.append(name_and_rooms[:])
return results
This will print out name even if there are no list of rooms to follow, e.g. [['bob'],['susan']].
Also, this will not merge repeated names, e.g. [['bob'],['123'],['bob'],['456']]. If that is desired, then you'll need to shove names into a temporary dict instead, with each room list as values to it. And then spit out the key-values of the dict at the end. But that on its own will not preserve the order of the names. If you care to preserve the order of the names, you can have another list that contains the order of the names and use that when spitting out the values in the dict.
Really, you should be using a dict for this. This assumes that the order of lists doesn't change (the name is always first).
As others suggested you should re-evaluate your data structure.
>>> from itertools import chain
>>> li_combo = list(chain.from_iterable(lst))
>>> d = {}
>>> for i in li_combo:
... if is_name(i):
... k = i
... if k not in d:
... d[k] = []
... else:
... d[k].append(i)
...
>>> final_list = [[k]+d[k] for k in d]
>>> final_list
[['Bob', '125A', '154B', '643A', '142C', '192B', '653G'], ['Carol', '95H', '123C'], ['David', '120G']]
reduce is your answer. Your data is this:
l=[['Bob'], ['125A', '154B', '643A'], ['142C', '192B'], ['653G'], ['Carol'], ['95H', '123C'], ['David'], ['120G']]
You say you've already got a function that determines if an element is a name. Here is my one:
import re
def is_name(s):
return re.match("[A-z]+$",s) and True or False
Then, using reduce, it is a one liner:
reduce(lambda c, n: is_name(n[0]) and c+[n] or c[:-1]+[c[-1]+n], l, [])
Result is:
[['Bob', '125A', '154B', '643A', '142C', '192B', '653G'], ['Carol', '95H', '123C'], ['David', '120G']]