search array of dictionaries and extract value - python

I got the following data:
[{'name': 'SqueezePlay', 'power': '1', 'playerid': '91:e2:b5:24:49:63', 'ip': '192.168.1.144:51346', 'canpoweroff': 1, 'displaytype': 'none', 'seq_no': '10', 'connected': 1, 'isplayer': 1, 'model': 'squeezeplay', 'uuid': 'fgh79fg7h98789798978'}, {'name': "FLX's iPhone", 'power': '1', 'playerid': '4c:32:d1:45:6c:4e', 'ip': '84.105.161.205:53972', 'canpoweroff': 1, 'displaytype': 'none', 'seq_no': 0, 'connected': 1, 'isplayer': 1, 'model': 'iPengiPod', 'uuid': '9791c009e3e7fghfg346456456'}]
I changed the values for privacy means.
I'd like to search the array based on "name" ("SqueezePlay" for example) and I'd to retrieve the "playerid" ("91:e2:b5:24:49:63" for example).
What would be the most efficient way to do this in Python? Thanks!

If your list of dicts is data, then you can try this:
next(d for d in data if d['name'] == 'SqueezePlay')['playerid']
This returns '91:e2:b5:24:49:63' (for the first occurence of the given name).
You have to define what to do if given name is not in your data.

Define a function to find a player based on its name:
def find_player(all_players, name):
for player in all_players:
if player['name'] == name:
return player['playerid']
This way (I'm guessing name is unique) you don't have to loop the whole list of players, instead, once you find it, return its playerid:
>>> p = [{'name': 'SqueezePlay', 'power': '1', 'playerid': '91:e2:b5:24:49:63', 'ip': '192.168.1.144:51346', 'canpoweroff': 1, 'displaytype': 'none', 'seq_no': '10', 'connected': 1, 'isplayer': 1, 'model': 'squeezeplay', 'uuid': 'fgh79fg7h98789798978'}, {'name': "FLX's iPhone", 'power': '1', 'playerid': '4c:32:d1:45:6c:4e', 'ip': '84.105.161.205:53972', 'canpoweroff': 1, 'displaytype': 'none', 'seq_no': 0, 'connected': 1, 'isplayer': 1, 'model': 'iPengiPod', 'uuid': '9791c009e3e7fghfg346456456'}]
>>> find_player(p, 'SqueezePlay')
'91:e2:b5:24:49:63'

The solutions posted by others work great if you are only searching the list once or a few times. If you will be searching it frequently, or if the list is more than a few items, and the names are guaranteed to be unique, it might pay off to make a dictionary from that list once, and then access the items by name in that dictionary. Or, if your program is making the list, put them in a dictionary to begin with. (If the order is important, i.e. you want to display the items in the order they were entered by a user, use a collections.OrderedDict.)
lyst = [{'name': 'SqueezePlay', 'power': '1', 'playerid': '91:e2:b5:24:49:63',
'ip': '192.168.1.144:51346', 'canpoweroff': 1, 'displaytype': 'none',
'seq_no': '10', 'connected': 1, 'isplayer': 1, 'model': 'squeezeplay',
'uuid': 'fgh79fg7h98789798978'}, {'name': "FLX's iPhone", 'power': '1',
'playerid': '4c:32:d1:45:6c:4e', 'ip': '84.105.161.205:53972',
'canpoweroff': 1, 'displaytype': 'none', 'seq_no': 0, 'connected': 1,
'isplayer': 1, 'model': 'iPengiPod', 'uuid': '9791c009e3e7fghfg346456456'}]
dyct = dict((item.pop("name"), item) for item in lyst)
# Python 3: {item.pop("name"): item for item in lyst}
print dyct["SqueezePlay"]
Note that the resulting dictionary no longer has name as a key of the nested dictionaries; it has been popped to avoid duplicating data in two places (if you keep it in two places, it's twice as much work to update it, and if you forget somewhere, they get out of sync). If you want to keep it, write this instead:
dyct = dict((item["name"], item) for item in lyst)
# Python 3: {item["name"]: item for item in lyst}

Related

How can I remove nested keys and create a new dict and link both with an ID?

I have a problem. I have a dict my_Dict. This is somewhat nested. However, I would like to 'clean up' the dict my_Dict, by this I mean that I would like to separate all nested ones and also generate a unique ID so that I can later find the corresponding object again.
For example, I have detail: {...}, this nested, should later map an independent dict my_Detail_Dict and in addition, detail should receive a unique ID within my_Dict. Unfortunately, my list that I give out is empty. How can I remove my slaughtered keys and give them an ID?
my_Dict = {
'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {
'selector': {
'number': '12312',
'isTrue': True,
'requirements': [{
'type': 'customer',
'requirement': '1'}]
}
}
}
def nested_dict(my_Dict):
my_new_dict_list = []
for key in my_Dict.keys():
#print(f"Looking for {key}")
if isinstance(my_Dict[key], dict):
print(f"{key} is nested")
# Add id to nested stuff
my_Dict[key]["__id"] = 1
my_nested_Dict = my_Dict[key]
# Delete all nested from the key
del my_Dict[key]
# Add id to key, but not the nested stuff
my_Dict[key] = 1
my_new_dict_list.append(my_Dict[key])
my_new_dict_list.append(my_Dict)
return my_new_dict_list
nested_dict(my_Dict)
[OUT] []
# What I want
[my_Dict, my_Details_Dict, my_Data_Dict]
What I have
{'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {'selector': {'number': '12312',
'isTrue': True,
'requirements': [{'type': 'customer', 'requirement': '1'}]}}}
What I want
my_Dict = {'_key': '1',
'group': 'test',
'data': 18,
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': 22}
my_Data_Dict = {'__id': 18}
my_Detail_Dict = {'selector': {'number': '12312',
'isTrue': True,
'requirements': [{'type': 'customer', 'requirement': '1'}]}, '__id': 22}
The following code snippet will solve what you are trying to do:
my_Dict = {
'_key': '1',
'group': 'test',
'data': {},
'type': '',
'code': '007',
'conType': '1',
'flag': None,
'createdAt': '2021',
'currency': 'EUR',
'detail': {
'selector': {
'number': '12312',
'isTrue': True,
'requirements': [{
'type': 'customer',
'requirement': '1'}]
}
}
}
def nested_dict(my_Dict):
# Initializing a dictionary that will store all the nested dictionaries
my_new_dict = {}
idx = 0
for key in my_Dict.keys():
# Checking which keys are nested i.e are dictionaries
if isinstance(my_Dict[key], dict):
# Generating ID
idx += 1
# Adding generated ID as another key
my_Dict[key]["__id"] = idx
# Adding nested key with the ID to the new dictionary
my_new_dict[key] = my_Dict[key]
# Replacing nested key value with the generated ID
my_Dict[key] = idx
# Returning new dictionary containing all nested dictionaries with ID
return my_new_dict
result = nested_dict(my_Dict)
print(my_Dict)
# Iterating through dictionary to get all nested dictionaries
for item in result.items():
print(item)
If I understand you correctly, you wish to automatically make each nested dictionary it's own variable, and remove it from the main dictionary.
Finding the nested dictionaries and removing them from the main dictionary is not so difficult. However, automatically assigning them to a variable is not recommended for various reasons. Instead, what I would do is store all these dictionaries in a list, and then assign them manually to a variable.
# Prepare a list to store data in
inidividual_dicts = []
id_index = 1
for key in my_Dict.keys():
# For each key, we get the current value
value = my_Dict[key]
# Determine if the current value is a dictionary. If so, then it's a nested dict
if isinstance(value, dict):
print(key + " is a nested dict")
# Get the nested dictionary, and replace it with the ID
dict_value = my_Dict[key]
my_Dict[key] = id_index
# Add the id to previously nested dictionary
dict_value['__id'] = id_index
id_index = id_index + 1 # increase for next nested dic
inidividual_dicts.append(dict_value) # store it as a new dictionary
# Manually write out variables names, and assign the nested dictionaries to it.
[my_Details_Dict, my_Data_Dict] = inidividual_dicts

How to retrieve data from dict in Python?

When I click on a treeview item it outputs
{'text': 1, 'image': '', 'values': [1, '3:18:00', 'pm'], 'open': 0, 'tags': ''}
How do I retrieve the specific values like the 1 or pm ? I used
queryResultTable.bind('<ButtonRelease-1>', select_item)
def select_item(a):
itemlibrary = queryResultTable.focus()
print(queryResultTable.item(itemlibrary))
I tried .get but couldn't really get anywhere
Try this:
a = {'text': 1, 'image': '', 'values': [1, '3:18:00', 'pm'], 'open': 0, 'tags': ''}
print(a['values'][0])
print(a['values'][2])
This should give you:
1
pm

Errors when trying to copy some data from a 2-D array to another

I am trying to write a simple program that reads and writes in xlsx files.
I have managed to import data from the file, and turn it into a dictionary.
But this array is arranged in a way that bothers me:
the array is something like this:
{
'position': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
'lastname': {0: 'Hamilton', 1: 'Bottas', 2: 'Verstappen', 3: 'Leclerc', 4: 'Vettel', 5: 'Albon'},
'firstname': {0: 'Lewis', 1: 'Valtteri', 2: 'Max', 3: 'Charles', 4: 'Sebastian', 5: 'Alexander'},
'team': {0: 'Mercedes', 1: 'Mercedes', 2: 'RedBull', 3: 'Ferrari', 4: 'Ferrari', 5: 'RedBull'}
}
(using F1 drivers as an example, don't pay attention)
I would like to transform this array into something that looks more like this:
{
0: {'position': 1, 'lastname': 'Hamilton', 'firstname': 'Lewis', 'team': 'Mercedes'},
1: {'position': 2, 'lastname': 'Bottas', 'firstname': 'Valtteri', 'team': 'Mercedes'},
2: {'position': 3, 'lastname': 'Verstappen', 'firstname': 'Max', 'team': 'RedBull'},
...
}
So that I could use the following code
for data in array:
print(array[data])
to print ALL the data on Lewis Hamilton,
then ALL the data on Valtteri Bottas, etc
and not
positions of ALL drivers
names of ALL drivers
So, basically my array is like this
data[rowname][driver]
and I want it this way
data[driver][rowname]
My code below, trying to transfer data from an array named data
to an array named drivers
import pandas
import_file_path = "test.xlsx"
data = pandas.read_excel(import_file_path)
data = pandas.DataFrame(data)
data = data.to_dict()
newdriver = {}
drivers = {}
lines = 0
# getting number of lines in the array
for a in data:
for line in data[a]:
lines += 1
break
# for as many drivers as present in the array
for i in range(lines):
for column in data:
newdriver[column] = data[column][i]
# store the driver's data in a temporary variable, field by field
drivers[i] = newdriver
# storing driver data in a row of our final array
print(drivers)
The final print statement results in this:
{0: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'},
1: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'},
2: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'},
3: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'},
4: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'},
5: {'position': 6, 'lastname': 'Albon', 'firstname': 'Alexander', 'team': 'RedBull'}}
The same driver in every line of the array.
I've investigated the issue, and it would seem that this line:
newdriver[column] = data[column][i]
also updates the data in the drivers array.
Halfway through the process of storing data into newdriver, "Lewis Bottas" appears in the drivers table, when I'm not even editing it.
Which makes no sense to me. (Lewis Bottas isn't a real driver)
{0: {'position': 2, 'lastname': 'Bottas', 'firstname': 'Lewis', 'team': 'Mercedes'}}
I suspect that the drivers[i] = newdriver makes drivers and newdriver share the same memory address, and thus, updates their values at the same time.
It's like I've created a pointer without wanting to. I just want to copy the values, not make them share the same address.
Any sort of help is welcome.
If you don't want to import anything
Tested on:
Python 2.7 , 3.7.5
def convert(array):
newArray = {}
for index in range(0,len(v['position'])):
newArray[index] = {
'position': array['position'][index],
'lastname': array['lastname'][index],
'firstname': array['firstname'][index],
'team': array['team'][index]};
return newArray
Edit: To do this Dynamically
# Input must be { StringOrNumber: { Number: SomeValue }, etc }
def convertDynamically(array):
newArray = {}
mainKeys = list(v.keys())
for index in range(0,len(v[mainKeys[0]])):
tmp = {}
for key in mainKeys:
tmp[key] = array[key][index]
newArray[index] = tmp
return newArray

How to modify the values of a dictionary from a list of dictionaries

I am declaring a method named add_to_cart(db, itemid, quantity). Whenever the method is called, it will look into the database for the session data. Session data contains a list of dictionaries. The purpose of this method is to create a new entry (dictionary) to the list or update the value of existing entry.
Dictionary has the following keys: id, quantity
So far I have developed the following code. First of all after fetching the data from the database, I am matching the itemid with the dictionary key: 'id'. If the itemid does not match with any of the values of the dictionaries, then it will append a new dictionary to that list.
def add_to_cart(db, itemid, quantity):
# ......
row = cursor.fetchone()
if row is not None:
cart = json.loads(row['data'])
for dic in cart:
if str(dic.get("id")) == str(itemid):
dic['quantity'] = int(dic['quantity']) + quantity
data = json.dumps(cart)
# update the 'data' to the database
break
else:
if counter == len(cart):
item = {
'id': itemid,
'quantity': quantity
}
cart.append(item)
data = json.dumps(cart)
# update the 'data' to the database
break
Let the initial cart is like:
[{'id': '40', 'quantity': '2'}, {'id': '41', 'quantity': '5'}]
When I add 1 more of the item 40 to the cart, this should be like:
[{'id': '40', 'quantity': '3'}, {'id': '41', 'quantity': '5'}]
but I am getting :
[{'id': '40', 'quantity': '2'}, {'id': '41', 'quantity': '5'}, {'id': '40', 'quantity': '1'}]
You are adding a new dictionary to the list when you do cart.append(item),
hence the list
[{'id': '40', 'quantity': '2'}, {'id': '41', 'quantity': '5'}]
ends up being
[{'id': '40', 'quantity': '2'}, {'id': '41', 'quantity': '5'}, {'id': '40', 'quantity': '1'}]
But you want to find the matching id in that list of dictionaries, and add to the quantity for that dictionary.
So the code will look like as below:
li = [{'id': '40', 'quantity': '2'}, {'id': '41', 'quantity': '5'}]
def add_elem(li, id, to_add):
#Iterate over the dictionaries
for item in li:
#If the id is found
if str(id) in item.values():
#Increment the quantity
item['quantity'] = str(int(item['quantity']) + to_add)
#Return the updated list
return li
print(add_elem(li, 40, 1))
The output will be
[{'id': '40', 'quantity': '3'}, {'id': '41', 'quantity': '5'}]
The problem seems to be that you are going simply adding a new dict to the list (cart), through append. You need to go through the list, find the dict with the itemid you need, then add to the quantity.
Try this -
for dict in cart:
if dict[itemid] == itemid:
dict['quantity'] += str(quantity)
break
item = {
'id': itemid,
'quantity': quantity
}
cart.append(item)

How to updating dict items inside a list of dicts

I have this list of dicts that I'm maintaining as a master list:
orig_list = [
{ 'cpu': '4', 'mem': '4', 'name': 'server1', 'drives': '4', 'nics': '1' }
{ 'cpu': '1', 'mem': '2', 'name': 'server2', 'drives': '2', 'nics': '2' }
{ 'cpu': '2', 'mem': '8', 'name': 'server3', 'drives': '1', 'nics': '1' }
]
However, I need to perform actions on things inside this list of dicts, like:
def modifyVM(local_list)
local_temp_list = []
for item in local_list :
'''
Tons of VM processy things happen here.
'''
item['cpu'] = 4
item['notes'] = 'updated cpu'
local_temp_list.append(item)
return local_temp_list
temp_list []
for item in orig_list :
if item['cpu'] < 4
temp_list.append(item)
result_list = modifyVM(temp_list)
At this point, result_list contains:
result_list = [
{ 'cpu': '4', 'mem': '2', 'name': 'server2', 'drives': '2', 'nics': '2' }
{ 'cpu': '4', 'mem': '8', 'name': 'server3', 'drives': '1', 'nics': '1' }
]
So my questions are:
1) What is the most efficient way to update orig_list with the results of result_list? I'm hoping to end up with:
orig_list = [
{ 'cpu': '4', 'mem': '4', 'name': 'server1', 'drives': '4', 'nics': '1' }
{ 'cpu': '4', 'mem': '2', 'name': 'server2', 'drives': '2', 'nics': '2' 'notes': 'updated cpu' }
{ 'cpu': '4', 'mem': '8', 'name': 'server3', 'drives': '1', 'nics': '1' 'notes': 'updated cpu' }
]
2) Is there a way to update orig_list without ever creating secondary lists?
Thank you in advance.
Collections store references to the objects.
So the code you posted is already modifying the items in "orig_list" as well, cause all the lists are storing references to the same original dictionaries.
As for the second part of your question, you don't need to create a new list. You can modify the objects directly, and next time you iterate the list you'll see the updated values.
Like for example:
orig_list = [
{ 'cpu': 4, 'mem': '4', 'name': 'server1', 'drives': '4', 'nics': '1' },
{ 'cpu': 1, 'mem': '2', 'name': 'server2', 'drives': '2', 'nics': '2' },
{ 'cpu': 2, 'mem': '8', 'name': 'server3', 'drives': '1', 'nics': '1' }
]
print orig_list
for item in orig_list :
if item['cpu'] < 4:
item['cpu'] = 4
print orig_list
Output of first print:
[{'mem': '4', 'nics': '1', 'drives': '4', 'cpu': 4, 'name': 'server1'},
{'mem': '2', 'nics': '2', 'drives': '2', 'cpu': 1, 'name': 'server2'},
{'mem': '8', 'nics': '1', 'drives': '1', 'cpu': 2, 'name': 'server3'}]
And second print:
[{'mem': '4', 'nics': '1', 'drives': '4', 'cpu': 4, 'name': 'server1'},
{'mem': '2', 'nics': '2', 'drives': '2', 'cpu': 4, 'name': 'server2'},
{'mem': '8', 'nics': '1', 'drives': '1', 'cpu': 4, 'name': 'server3'}]
No, you don't need to create a separate list, just use list comprehension.
Just iterate through the list and check if value of cpu key is less than 4. If value is less than 4, then update value the cpu key to 4 and add an extra key notes having value as 'updated_cpu'. Value of orig_list after iteration finishes is the desired result.
>>> orig_list = [{'cpu': 4, 'drives': '4', 'mem': '4', 'name': 'server1', 'nics': '1'},
{'cpu': 1, 'drives': '2', 'mem': '2', 'name': 'server2', 'nics': '2'},
{'cpu': 2, 'drives': '1', 'mem': '8', 'name': 'server3', 'nics': '1'}]
>>> for item in orig_list:
if item['cpu']<4:
item['cpu']=4
item['notes'] = 'updated cpu'
>>> orig_list
[{'cpu': 4, 'drives': '4', 'mem': '4', 'name': 'server1', 'nics': '1'},
{'cpu': 4, 'drives': '2', 'mem': '2', 'name': 'server2', 'nics': '2', 'notes': 'updated cpu'},
{'cpu': 4, 'drives': '1', 'mem': '8', 'name': 'server3', 'nics': '1', 'notes': 'updated cpu'}]
Thank you for all the input! I flagged eugenioy's post as the answer because he posted first. Both the answer from him and from Rahul Gupta are very efficient ways to update a list of dictionaries.
However, I kept trying other ways because these answers, as efficient as they are, still do one other thing I've always been told is taboo: modifying the list you're iterating over.
Keep in mind, that I'm still learning Python. So if some of my "revelations" here are mundain, they are new and "wow" to me. To that effect, I'm adding the answer that I actually ended up implementing.
Here's the finished code:
def modifyVM(local_list, l_orig_list)
for item in local_list[:] :
l_orig_list.remove(item)
'''
Tons of VM processy things happen here.
'''
item['cpu'] = 4
item['notes'] = 'updated cpu'
l_orig_list.append(item)
temp_list []
for item in orig_list[:] :
if item['cpu'] < 4
temp_list.append(item)
modifyVM(temp_list, orig_list)
I change this line:
def modifyVM(local_list)
To this line:
def modifyVM(local_list, l_orig_list)
In this way, I'm passing in both the list I want to use as well as the list I want to update.
Next I changed:
for item in local_list :
To this line:
for item in local_list[:] :
This causes "item" to iterate through a slice (copy) of "local_list" that contains everything.
I also added:
l_orig_list.remove(item)
And:
l_orig_list.append(item)
This solved several problems for me.
1) This avoids the potential of modifying any list that's being iterated through.
2) This allows "orig_list" to be updated as processes are happening, which cuts down on the "secondary lists" that are created and maintained.
3) The "orig_list" that's passed into the function and "l_orig_list" are linked variables until a hard assignment (i.e. l_orig_list = 'anything') is made. (Again, thank you to everyone that answered! This was some great "secret sauce" learning for me, and y'all pointed it out.) So, avoiding the "=", I'm able to update "l_orig_list" and have "orig_list" updated as well.
4) This also allows the movement of items from one list to another if needed (i.e. list items that generate errors can be removed from "orig_list" and placed in any other list, like "bad_list" for example.
In closing, I'd like to give recognition to Steven Rumbalski. When I read your comment, I was like, "Of course!!!" However, I spent 2 days on it before realizing that dictionaries cannot be sorted. I had to narrow down the technical problem I was facing to ask a question here. Sorting was an unstated requirement for other parts of the script. So GREAT suggestion, and I'll probably use that for another script.

Categories