Why is my For loop in python printing three times? - python

I am trying to loop through a JSON file to get specific values, however, when doing so the loop is printing three times. I only want the value to print once and have tried breaking the loop but it still has not worked.
Python Code:
with open(filename) as json_filez:
dataz = json.load(json_filez)
for i in dataz:
for i in dataz['killRelated']:
print(i["SteamID"])
break
and a snippet of my json file is
{
"killRelated": [
{
"SteamID": "76561198283763531",
"kill": "15,302",
"shotacc": "16.1%"
}
],
"metaData": [
{
"test": "lol"
}
],
"miscData": [
{
"damageGiven": "2,262,638",
"gamePlayed": "1,292",
"moneyEarned": "50,787,000",
"score": "31,122",
"timePlayed": "22d 11h 56m"
}
]
}
and this is my output:
76561198283763531
76561198283763531
76561198283763531
Expected output:
76561198283763531

The return from json.load is a dictionary, and you are only interested in one entry in that, keyed by 'killRelated'. Now the "values" against each dictionary entry are lists, so that is what you need to be iterating though. And each element of such a list is a dictionary that you can again access via a key.
So your code could be:
with open(filename) as json_filez:
dataz = json.load(json_filez)
for kr in dataz['killRelated']: # iterate through the list under the top-level keyword
print (kr["SteamID"])
Now in your sample data, there's only one entry in the dataz['killRelated'] list, so you'll only get that one printed. But in general, you should expect multiple entries - and cater for the possibility of none. You can handle that by try/except of by checking key existence; here's the latter:
with open(filename) as json_filez:
dataz = json.load(json_filez)
if 'killRelated' in dataz: # check for the top keyword
for kr in dataz['killRelated']: # iterate through the list under this keyword
if 'steamID' in kr: # check for the next level keyword
print (kr["SteamID"]) # report it
You were getting three output lines because your outer loop iterated across all keyword entries in dataz (although without examining them), and then each time within that also iterated across the dataz['killRelated'] list. Your addition of break only stopped that inner loop, which for the particular data you had was redundant anyway because it was only going to print one entry.

Your code is correct. You should check your json file or you can share your full JSON text. That would be a problem. I run you code with json snippet you provided and it works as expected.
import json
with open("test.json") as json_filez:
dataz = json.load(json_filez)
for i in dataz:
for i in dataz['killRelated']:
print(i["SteamID"])
and the result as blow:
76561198283763531

Related

How would I find a value from a json file that has been dumped before?

I might not be asking this correctly, since I'm quite new to json. I have this code:
data = {
"temp1": [
{"temp2": "!"},
{"temp3": "123"}
]
}
with open("./Config/test.json", "w") as f:
json.dump(data, f, indent=1)
with open("./Config/test.json", "r") as f:
data = json.load(f)
print(data["temp1"]["temp2"])
There will be more dictionaries like this with their unique id. The id is now labeled as temp1. How can I return the temp2 or temp3 value, without returning the whole json?
data = {
"temp1": [
{"temp2": "!"},
{"temp3": "123"}
]
}
...
data["temp1"]["temp2"] doesn't return "!", but an error Command raised an exception: TypeError: list indices must be integers or slices, not str
If you are asking how to solve this problem, I suggest you break what you are trying to do in separate pieces:
print(data["temp1"])
This will print a list. The reason you get an error is because you attempt to index that list with a string, but you must use an integer instead:
print(data["temp1"][0])
Now this will print out the first dict in the list which you can then index with a string:
print(data["temp1"][0]["temp2"])
As Barmar posted in his answer, you may need to loop over the list given by data["temp1"] to do what you need. Your question doesn't include enough details for me to recommend if that's the correct approach.
The key thing here is to follow the shape of the data and index it appropriately. As I show above, it often helps to add one index at a time. You should even consider assigning each piece to a variable.
You can use a loop to find temp2 in the list of dictionaries.
with open("./Config/test.json", "r") as f:
data = json.load(f)
for item in data["temp1"]:
if "temp2" in item:
print(item["temp2"])
it would be easier if you had a single dictionary rather than a list of dictionaries with different keys, e.g.
data = {
"temp1": {
"temp2": "!",
"temp3": "123"
}
}

sort values from a dictionary/json file

I've got this discord.py command that makes a leaderboard from a json
cogs/coins.json (the dictionary) looks like this:
{
"781524858026590218": {
"name": "kvbot test platform",
"total_coins": 129,
"data": {
"564050979079585803": {
"name": "Bluesheep33",
"coins": 127
},
"528647474596937733": {
"name": "ACAT_",
"coins": 2
}
}
(The green strings with numbers in the json files are discord guild/member ids)
How do I make the code shorter and clearer?
Thanks for helping in advance, because I really don't know the solution
When it comes to finding (sorting) the first ten items within a dict, then the way is much easier than repeatedly going through the dict and doing different things there.
And little better code, like Dict.get for safety access.
Based on a sample of JSON data.
with open('cogs/coins.json', 'r') as f:
coins_data = json.load(f)
# Get is safefy access to dict
# Dict.items() returns pairs of (Key, Val)
members_coins = list(coins_data.get(str(ctx.guild.id), None)['data'].items())
if members_coins is None: # If data not found
await ctx.send('Not data')
return
# Sort list by Val part of pair, and `coins` key, reverse for descending
members_coins.sort(key=lambda x: x[1]['coins'], reverse=True)
output = ''
# list[:10] for first 10 items (if list is smaller, thats okay, python don't mind)
for member_id, vals in members_coins[:10]:
output += f'{vals["name"]}: {vals["coins"]}'
# output += f'<#{member_id}>: {vals["coins"]}' # If you want "mention" display of user
await ctx.send(output)

Nested dictionary access is returning a list

The issue I'm having is that when i try to access the values within a nested dictionary, i cannot because it's returning a list instead of a dictionary.
I have a .json file with this format;
{
"users": [
{
"1": {
"1": "value",
"2": "value"
}
}
]
}
I load the .json file, and access the value i want by using this function
def load_json(fn):
with open(fn) as pf:
data = json.load(pf)
return data['users']['1']['2']
If i simply do return data it is a dictionary, but if try to access further by adding ['users'], it turns into a list and will give an index error if i try to access key #1 or #2 inside of that..
My objective is to obtain the value of the nested key #2 for example, ideally without having loop through it.
Your JSON contains an array (Python list) wrapping the inner dicts (that's what the [ and ] in your JSON indicate). All you need to do is change:
return data['users']['1']['2']
to:
return data['users'][0]['1']['2']
# ^^^ Added
to index the list to get into the inner dicts.
given your data structure, and following it down :
data is a dictionary - with one key 'users' and a value of a list
data['users'] is a list - with one entry
data['users'][0] is a dictionary - with one key '1' and a value of a dictionary
data['users'][0][1] is a dictionary - with two keys '1' and '2'
So you need to do do :
def load_json(fn):
with open(fn) as pf:
data = json.load(pf)
return data['users'][0]['1']['2']

add multiple json field by python

I have a json file like:
{
"parentNode": {
"id": "root",
"mbs": [
16995.9859862176,
-6029.919928079834,
-4.6344976928710935,
4674.872691701428
]
},
"children": [
{
"id": "00t2",
"mbs": [
16561.761031809023,
-5189.992543469676,
5,
221.7414398051216
]
},
{
"id": "01t2",
"mbs": [
16851.244334748077,
-5189.992543469676,
5,
221.7414398051216
]
}
]
}
Now I want to change the mbs value but to take a log before for roll back.
so my code is like:
if jsondict['parentNode']:
mbsx=jsondict['parentNode']['mbs'][0]
mbsy=jsondict['parentNode']['mbs'][1]
nodeid=jsondict['parentNode']['id'] #log the node id and mbs value
jsondict['mymbs_parent']=[nodeid,mbsx,mbsy] #write em down
jsondict['parentNode']['mbs'][0]=mbsx+xoffset #then change the value
jsondict['parentNode']['mbs'][1]=mbsy+yoffset
That works fine for parent node,
but there maybe many children nodes, so for the children part, the code is like this:
if jsondict['children']:
count=len(jsondict['children'])
for i in range(count):
mbsx=jsondict['children'][i]['mbs'][0]
mbsy=jsondict['children'][i]['mbs'][1]
nodeid=jsondict['children'][i]['id']
jsondict['mymbs_children'][i]=(nodeid,mbsx,mbsy)
jsondict['children'][i]['mbs'][0]=mbsx+xoffset
jsondict['children'][i]['mbs'][1]=mbsy+yoffset
then I get list assignment index out of range error.
I guess there isn't mymbs_children in the json file yet so there isn't jsondict['mymbs_children'][i]
I haven't found out how to do it, any ideas?
I don't love your approach, but for simplicity I can answer the question you have asked. To fix the error you are getting, add
jsondict['mymbs_children'] = []
as the first line in the block after
if jsondict['children']:
This will create the list which you are then trying to write to.
Then you need to use .append() to add the items to the list:
jsondict['mymbs_children'].append((nodeid,mbsx,mbsy))
instead of
jsondict['mymbs_children'][i]=(nodeid,mbsx,mbsy)
you can put an if condition surrounding the for loop to check:
...
if count > 0:
for i in range(count):
...
for python 3.8 onwards, you can use walrus operator as well to cut down the number of lines of code: (for more info)
...
if (count := len(jsondict['children'])) > 0:
for i in range(count):
...
another way which I prefer is to not use range:
...
for child in jsondict['children']:
child['mbs'][blah]...
...
this will avoid the issue of out of range index, and you might not even need to use the count variable
# Key "mymbs_children" is not present in the dictionary yet,
# so you'll need to declare it first and initialise to an empty list.
jsondict['mymbs_children'] = []
if jsondict['children']:
count=len(jsondict['children'])
for i in range(count):
mbsx=jsondict['children'][i]['mbs'][0]
mbsy=jsondict['children'][i]['mbs'][1]
nodeid=jsondict['children'][i]['id']
# Use append to add the tuple to this list
jsondict['mymbs_children'].append((nodeid,mbsx,mbsy));

How do I reiterate over the last iteration?

I have an unordered dict/json object of data. In fact I have many of those, line by line in a file. There are three keys/objects in each one. I never know which of the three has the data I need to add right back onto the other two. I cannot control how the data is initially written, whether I like it or not.
Currently I iterate over each of the three keys/objects until I find the correct key that has the fields I need. I then save them off to variables. Now, how do I go right back over the other two keys/objects that I might of already iterated over and add the fields and values back into them? As I said there are multiples of these from a file so it will just keep going on to the next one if I don't... reiterate?
Code:
with open(inputfile) as f:
for line in f:
try:
# File is one big json object per line. Load up the current line as JSON.
line = json.loads(line)
for result in line['scan_result']:
# Check if this object's filename field has the extra data I need to parse out and palce in t he others.
if "meta_data" in file_result['filename']:
print "FOUND METADATA"
#print result['filename']
regmatch = re.match(".*meta_data_(.+?)_(.+?):(.+?)$", file_result['filename'])
if regmatch:
print "REG MATCH -------------"
#print regmatch.groups()
timecreated = regmatch.group(1)
author = regmatch.group(2)
mime_type = regmatch.group(3)
So as you can see, I have the data pulled out. I just need to figure out how to put it back into the JSON objects I just iterated over. I'm open to doing this other ways to. Maybe sorting the object first and then running through it?
If it helps, the data structure looks like this. The order of the parent is never known though. This is one "line" (json object) in the file:
{
"filename": abc.gif
id : 13241
parent : 999
interesting_file_stuff : {
stuff : 123
stuff2 : 456
}
}
{
"filename": hello.zip+meta_data_stuff_here
id : 999
parent : NA
interesting_file_stuff : {
stuff : 5435
stuff2 : 24223
}
}
{
"filename": xyz.exe
id : 8342
parent : 999
interesting_file_stuff : {
stuff : 2
stuff2 : 3232
}
}
Add an extra boolean while loop.
You could have an extra loop which is while True, repeat until you use a break statement, then the outer loop will increment to the next value.
for line in f:
while True:
# do stuff
if condition:
break
# do more stuff
Looks like for loops can't go backwards, so I'll have to manually loop through with a while loop, giving complete control of the iterations and which way I go.

Categories

Resources