Pulling Values from a JSON Dataset that Match a Keyword - python

Currently working on a school project based on a JSON dataset about a weapon list in a videogame. I've been trying to add functionality where the user can click a button that runs code filtering the dataset down to the forms containing the key-word. Below is the back end code for one of the functions, returning all forms where name = dagger
def wpn_dagger():
with open('DSweapons.json', encoding='utf-8') as outfile:
data = json.load(outfile)
wpn_list = []
for dict in data:
if dict in ['name'] == 'dagger':
wpn_list.append(data)
print(wpn_list)
return wpn_list
Whilst I do not get any errors when I run the code the only output to the terminal is an empty set of [] brackets. Any help on this issue would be much appreciated.

if dict in ['name'] == 'dagger' is wrong syntax for what you want
when written like that ['name'] is a list containing the string 'name', so dict in ['name'] is checking if dict is in that list (will be always false) and then we check if the result of that == 'dagger', i.e. the whole thing reads as if False == 'dagger'
Try this:
def wpn_dagger():
with open('DSweapons.json', encoding='utf-8') as outfile:
data = json.load(outfile)
wpn_list = []
for weapon in data:
if weapon['name'] == 'dagger':
wpn_list.append(weapon)
print(wpn_list)
return wpn_list

Indentation in python is very important. Your for loop needs to be indented so it operates inside the with context manager.
Secondly, dict is a keyword in python, so use something different if you need to name a variable.
Finally you can get an object out of your form using .get('name') on the dictionary, it's at least easier to read in my opinion.
In summary, something like this (not tested):
def wpn_dagger():
with open('DSweapons.json', encoding='utf-8') as outfile:
data = json.load(outfile)
wpn_list = []
for form in data:
if form.get('name') == 'dagger':
wpn_list.append(form)
print(wpn_list)
return wpn_list

Related

Python function inside with open file: only the first list comprehention 'called' working properly? [duplicate]

This question already has an answer here:
Python Loop Only Iterating once with Large loop which takes input from a file
(1 answer)
Closed last year.
I have a csv file with multiple and repeated values. Mounted a giant list with lots of dictionaries with each value of list with csv.DictReader, separated the fields like this: (fieldnames=['person', 'food', 'weekday']).
I don't think the csv file content matters for the problem, so I'm not copying it.
Mostly of problems, I just needed to find a filtered value, so I used a Counter and/or list with comprehension.
Right now I need to find the difference between all possible 'food' and the 'food' consumed by one specific 'person'.
So I need to compare two list with list comprehension.
My code:
import csv
from typing import Counter
def analyze_log(path_to_file):
with open(path_to_file) as f:
csv_opened_file = csv.DictReader(
f, fieldnames=['name', 'food', 'weekday'])
def person_favorite_meal(opened_file, person):
person_orders = Counter(
order['food'] for order in opened_file if order['name'] == person
) # this works
return max(person_orders, key=person_orders.get)
def person_how_many_ordered(opened_file, person, meal):
person_orders = Counter(
order['food'] for order in opened_file if order['name'] == person and order['food'] == meal
) # this works
return person_orders[meal]
def person_never_ordered(opened_file, person):
every_ordered_meal = Counter([order['food'] for order in opened_file]) # this works
person_ordered_meals = Counter([
order['food'] for order in opened_file if order['name'] == person
]) # this DOESN'T work
list_difference = [
meal for meal in every_ordered_meal if meal not in person_ordered_meals]
return f"1- {person_ordered_meals} 2- {every_ordered_meal}"
print(person_never_ordered(csv_opened_file, 'maria'))
analyze_log('../data/orders_1.csv') # the file is read
The problem: only the first list in the function 'person_never_ordered' works properly.
Like if I have the code like this:
def person_never_ordered(opened_file, person):
every_ordered_meal = Counter([order['food'] for order in opened_file]) # this comes first, it works
person_ordered_meals = Counter([
order['food'] for order in opened_file if order['name'] == person
]) # this DOESN'T work
list_difference = [
meal for meal in every_ordered_meal if meal not in person_ordered_meals]
return f"1- {person_ordered_meals} 2- {every_ordered_meal}"
print(person_never_ordered(csv_opened_file, 'maria'))
analyze_log('../data/orders_1.csv') # the file is read
I get the console like this:
1- Counter() 2- Counter({'hamburguer': 33, 'pizza': 8, 'coxinha': 8,
'misto-quente': 7})
But if I just switch the lists order, with code like this:
def person_never_ordered(opened_file, person):
person_ordered_meals = Counter([
order['food'] for order in opened_file if order['name'] == person
]) # now this comes first, and it works
every_ordered_meal = Counter([order['food'] for order in opened_file]) # now this comes second first, and it doesn't work!
list_difference = [
meal for meal in every_ordered_meal if meal not in person_ordered_meals]
return f"1- {person_ordered_meals} 2- {every_ordered_meal}"
print(person_never_ordered(csv_opened_file, 'maria'))
analyze_log('../data/orders_1.csv') # the file is read
I get the console with this:
1- Counter({'hamburguer': 16, 'pizza': 8, 'coxinha': 8}) 2- Counter()
Why is this happening?
you cant call a function that is defined inside another function like that make it you have to define your functions globally in order to be able to use them in the rest of your code
fix:
move the definition outside the analyze function
or try adding global before each definition

Read csv to dict of lists of dicts

I have a data set with tons of (intentional) duplication. I'd like to collapse(?) that to make it better suited for my needs. The data reads like this:
Header1, Header2, Header3
Example1, Content1, Stuff1
Example1, Content2, Stuff2
Example1, Content3, Stuff3
Example2, Content1, Stuff1
Example2, Content5, Stuff5
etc...
And I want that to end up as a dict with column one's values as keys and lists of dicts as values to those keys like so:
{Example1 : [{Header2:Content1, Header3:Stuff1}, {Header2:Content2, Header3:Stuff2}, {Header2:Content3, Header3:Stuff3}],
Example2 : [{Header2:Content1, Header3:Stuff1}, {Header2:Content5, Header3:Stuff5}]}
I'm brand new to Python and a novice programmer over all so feel free to get clarification if this question is confusing. 😅 Thanks!
Update I was rightfully called out for not posting my example code (thanks for keeping me honest!) so here it is. The code below works but since I'm new to Python I don't know if it's well written or not. Also the dict ends up with the keys (Example1 and Example2) in reverse order. That doesn't really matter but I do not understand why.
def gather_csv_info():
all_csv_data = []
flattened_data = {}
reading_csv = csv.DictReader(open(sys.argv[1], 'rb'))
for row in reading_csv:
all_csv_data.append(row)
for row in all_csv_data:
if row["course_sis_ids"] in flattened_data:
flattened_data[row["course_sis_ids"]].append({"user_sis_ids":row["user_sis_ids"], "file_ids":row["file_ids"]})
else:
flattened_data[row["course_sis_ids"]] = [{"user_sis_ids":row["user_sis_ids"], "file_ids":row["file_ids"]}]
return flattened_data
This code works but I don't know how pythonic it is and I don't understand why the flattened_data dict has the keys in reverse order that they appear in the original CSV. It doesn't strictly matter that they're not in order, but it is curious.
def gather_csv_info():
all_csv_data = []
flattened_data = {}
reading_csv = csv.DictReader(open(sys.argv[1], 'rb'))
for row in reading_csv:
all_csv_data.append(row)
for row in all_csv_data:
if row["course_sis_ids"] in flattened_data:
flattened_data[row["course_sis_ids"]].append({"user_sis_ids":row["user_sis_ids"], "file_ids":row["file_ids"]})
else:
flattened_data[row["course_sis_ids"]] = [{"user_sis_ids":row["user_sis_ids"], "file_ids":row["file_ids"]}]
return flattened_data
I completely changed the answer as you changed your question, so instead I just tidied the code in your own answer so it's more "Pythonic":
import csv
from collections import defaultdict
def gather_csv_info(filename):
all_csv_data = []
flattened_data = defaultdict(list)
with open(filename, 'rb') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
key = row["Header1"]
flattened_data[key].append({"Header2":row["Header2"], "Header3":row["Header3"]})
return flattened_data
print(gather_csv_info('data.csv'))
Not sure why you want the data in this format, but that's up to you.

When enumerating a list of tuples I get TypeError: 'type' object has no attribute '__getitem__'

The script below reads a CSV file and creates a list of tuples called people. The tuple is the patron_id (a unique string identifier) and a dictionary of person information.
For a sample of data (simplified with name info removed) looking like this:
patron_id show_id
1111MAIN MW1
2222SCOTT MW2
1111MAIN MW1
The script should output a list of tuples, that looks like this:
[
("1111MAIN", {'patron_id': "1111MAIN", 'show_list': ["MW1", "MW2"]}),
("2222SCOTT", {'patron_id': "2222SCOTT", 'show_list': ["MW2"]})
]
The script raises the following error in traceback:
File "frequency.py", line 75, in <module>
main("input.csv")
File "frequency.py", line 35, in main
person_index = [x for x, y in enumerate[people] if y[0] == patron_id]
When I test this line manually in the shell it returns the index of the tuple for which I am searching. Why is this line failing in the script?
import csv
def main(filename):
people = [] #list of person tuples
person = {} #patron info with list of unique shows
#open csv data
csv_file = csv.reader(open(filename, 'rb'))
#read and define CSV data
for record in csv_file:
show_list = []
patron_id = record[0]
first_name = record[1]
last_name = record[2]
show_name = record[3]
#check if this is the first row
if len(people) == 0:
show_list.append(show_name)
person = (patron_id, {'patron_id': patron_id, 'first_name': first_name, 'last_name': last_name, 'show_list': show_list})
people.append(person)
else:
#if this person IS in the list of people
if any(patron_id in person for person in people) == True:
#retrieve this person from people by finding its index
person_index = [x for x, y in enumerate[people] if y[0] == patron_id][0]
Well, as stated in my comment, the problem is that you try to call the __getitem__ method of enumerate by using square brackets, which is bound to fail because it does not have this method. To construct an enumerate object, use parentheses.
Try to pay close attention to the error messages, often they are helpful. I did not need to read your whole code to find the error.
You need to change enumerate[people] to enumerate(people). When you use brackets, python thinks you trying to access enumerate at position people. Instead, you need to call it.

How can I detect if a dictionary has certain criteria?

I'm trying to implement a save feature into my game to save a variety of information. The save part works perfectly fine, but the load part does not work. This is my current dictionary:
player_data = {'x':x, 'y':y, 'world':mapSection, 'introcomplete':homeintro}
I am loading/saving with this:
def save_game():
with open("savegame", "wb") as f:
pickle.dump(player_data, f)
def load_game():
with open("savegame", "rb") as f:
global player_data
player_data = pickle.load(f)
And to see what mapSection is set as, I use this:
load_game()
print(player_data)
if "1_1" in player_data:
print('maploaded')
game_map_1_1()
else:
print('reset')
game_intro()
But for some reason, it always skips over the if statement to the else statement. I don't know what I am doing wrong.
I'm guessing what you really want to do is check player_data['world'] == '1_1' and not '1_1' in player_data. The second one checks if you have a key named 1_1.
This is not specific to pickle.

file IO with defaultdict

I'm attempting to:
load dictionary
update/change the dictionary
save
(repeat)
Problem: I want to work with just 1 dictionary (players_scores)
but the defaultdict expression creates a completely seperate dictionary.
How do I load, update, and save to one dictionary?
Code:
from collections import defaultdict#for manipulating dict
players_scores = defaultdict(dict)
import ast #module for removing string from dict once it's called back
a = {}
open_file = open("scores", "w")
open_file.write(str(a))
open_file.close()
open_file2 = open("scores")
open_file2.readlines()
open_file2.seek(0)
i = input("Enter new player's name: ").upper()
players_scores[i]['GOLF'] = 0
players_scores[i]['MON DEAL'] = 0
print()
scores_str = open_file2.read()
players_scores = ast.literal_eval(scores_str)
open_file2.close()
print(players_scores)
You are wiping your changes; instead of writing out your file, you read it anew and the result is used to replace your players_scores dictionary. Your defaultdict worked just fine before that, even if you can't really use defaultdict here (ast.literal_eval() does not support collections.defaultdict, only standard python literal dict notation).
You can simplify your code by using the json module here:
import json
try:
with open('scores', 'r') as f:
player_scores = json.load(f)
except IOError:
# no such file, create an empty dictionary
player_scores = {}
name = input("Enter new player's name: ").upper()
# create a complete, new dictionary
players_scores[name] = {'GOLF': 0, 'MON DEAL': 0}
with open('scores', 'w') as f:
json.dump(player_scores, f)
You don't need defaultdict here at all; you are only creating new dictionary for every player name anyway.
I think one problem is that to index the data structure the way you want, something like a defaultdict(defaultdict(dict)) is what's really needed — but which unfortunately it's impossible to specify one directly like that. However, to workaround that, all you need to do is define a simple intermediary factory function to pass to the upper-level defaultdict:
from collections import defaultdict
def defaultdict_factory(*args, **kwargs):
""" Create and return a defaultdict(dict). """
return defaultdict(dict, *args, **kwargs)
Then you can use players_scores = defaultdict(defaultdict_factory) to create one.
However ast.literal_eval() won't work with one that's been converted to string representation because it's not one of the simple literal data types the function supports. Instead I would suggest you consider using Python's venerable pickle module which can handle most of Python's built-in data types as well custom classes like I'm describing. Here's an example of applying it to your code (in conjunction with the code above):
import pickle
try:
with open('scores', 'rb') as input_file:
players_scores = pickle.load(input_file)
except FileNotFoundError:
print('new scores file will be created')
players_scores = defaultdict(defaultdict_factory)
player_name = input("Enter new player's name: ").upper()
players_scores[player_name]['GOLF'] = 0
players_scores[player_name]['MON DEAL'] = 0
# below is a shorter way to do the initialization for a new player
# players_scores[player_name] = defaultdict_factory({'GOLF': 0, 'MON DEAL': 0})
# write new/updated data structure (back) to disk
with open('scores', 'wb') as output_file:
pickle.dump(players_scores, output_file)
print(players_scores)

Categories

Resources