What is the Python equivalent of a jagged array? - python-3.x

After years of using Excel and learning VBA, I am now trying to learn Python. Here's the scenario:
I asked 7 summer camp counselors which activities they would like to be in charge of. Each student had a random number of responses, and there is no upper limit on the number of activities chosen. However, each activity is unique, and once "claimed" by a student it cannot claimed by any other counselor. The results were:
Adam: archery, canoeing
Bob: frisbee, golf, painting, trampoline
Carol: tennis, dance, skating
Denise: cycling
Eddie: horseback, fencing, soccer
Fiona: painting
George: basketball, football
I'm most familiar with VB (I am an old guy) and in the past I would have stored the above info in a jagged array. But since I'm new to Python, I am confused as to how to do this. I think a list of lists would work for me and here is my code. Let's say I have a list of counselors, and separate lists for each counselors' activities. How do I merge them or put them in one data structure? What am I doing wrong below? Thank you.
counselors = []
counselors = ['Adam','Bob','Carol','Denise','Eddie','Fiona','George']
#create a list of Carol's activities
activities = []
activities = ['tennis','dance','skating']
counselors[2].append[(activities)]

A jagged array in Python is pretty much a list of lists as you mentioned.
I would use a dictionary to store the counselors activity information, where the key is the name of the counselor, and the value is the list of activities the counselor will be in charge of e.g.
counselors_activities = {"Adam": ["archery", "canoeing"],
"Bob": ["frisbee", "golf", "painting", "trampoline"],
"Carol": ["tennis", "dance", "skating"],
"Denise": ["cycling"],
"Eddie": ["horseback", "fencing", "soccer"],
"Fiona": ["painting"],
"George": ["basketball", "football"]}
And access each counselor in the dictionary as such:
counselors_activites["Adam"] # when printed will display the result => ['archery', 'canoeing']
In regards to the question, I would store the list of activities available in a list, and anytime an activity is chosen, remove it from the list and add it to the counselor in the dictionary as such:
list_of_available_activities.remove("archery")
counselors_activities["Adam"].append("archery")
And if a counselor no longer was in charge of the activity, remove it from them and add it back to the list of available activities.
Update: I have provided a more fully fledged solution below based on your requirements from your comments.
Text file, activites.txt:
Adam: archery, canoeing
Bob: frisbee, golf, painting, trampoline
Carol: tennis, dance, skating
Denise: cycling
Eddie: horseback, fencing, soccer
Fiona: painting
George: basketball, football
Code:
#Set of activities available for counselors to choose from
set_of_activities = {"archery",
"canoeing",
"frisbee",
"golf",
"painting",
"trampoline",
"tennis",
"dance",
"skating",
"cycling",
"horseback",
"fencing",
"soccer",
"painting",
"basketball",
"football"}
with open('activities.txt', 'r') as f:
for line in f:
# Iterate over the file and pull out the counselor's names
# and insert their activities into a list
counselor_and_activities = line.split(':')
counselor = counselor_and_activities[0]
activities = counselor_and_activities[1].strip().split(', ')
# Iterate over the list of activities chosen by the counselor and
# see if that activity is free to choose from and if the activity
# is free to choose, remove it from the set of available activities
# and if it is not free remove it from the counselor's activity list
for activity in activities:
if activity in set_of_activities:
set_of_activities.remove(activity)
else:
activities.remove(activity)
# Insert the counselor and their chosen activities into the dictionary
counselors_activities[counselor] = activities
# print(counselors_activities)
I have made one assumption with this new example, which is that you will already have a set of activities that can be chosen from already available:
I made the text file the same format of the counselors and their activities listed in the question, but the logic can be applied to other methods of storage.
As a side note and a correction from my second example previously, I have used a set to represent the list of activities instead of a list in this example. This set will only be used to verify that no counselor will be in charge of an activity that has already been assigned to someone else; i.e., removing an activity from the set will be faster than removing an activity from the list in worst case.
The counselors can be inserted into the dictionary from the notepad file without having to insert them into a list.
When the dictionary is printed it will yield the result:
{"Adam": ["archery", "canoeing"],
"Bob": ["frisbee", "golf", "painting", "trampoline"],
"Carol": ["tennis", "dance", "skating"],
"Denise": ["cycling"],
"Eddie": ["horseback", "fencing", "soccer"],
"Fiona": [], # Empty activity list as the painting activity was already chosen by Bob
"George": ["basketball", "football"]}

Related

How can I keep track of all the objects I created of a class, in a list in the driver program?

I need to make a list that tracks the objects that have been called of a certain class.
These objects need to be accessible and display information when called from the list.
pizza2 = DeluxePizza("small",1,1,1,1,1)
pizza2.__str__()
pizza3 = DeluxePizza("medium",1,2,3,0,2)
pizza3.__str__()
pizza4 = DeluxePizza("large",1,0,1,0,5)
pizza4.__str__()
Whenever a new object is called, they need to be stored in the list like this-
pizzas = [pizza2, pizza3, pizza4]
I think you want to do something along the lines of the following:
pizza_orders = []
pizzas = [("small",1,1,1,1,1),("medium",1,2,3,0,2),("large",1,0,1,0,5)]
for pza in pizzas:
pizza_orders.append(DeluxePizza("small",pza[0],pza[1],pza[2],pza[3],pza[4]))
for po in pizza_orders:
print(po)
pizza_orders is a list of DeluxePizza Class instances
pizzas is a list of the specific instantiations of the Deluxe Pizza class you want to order
When you execute the po in pizza_orders list you will get the list of orders for the three pizzas

How do I process metadata on titles in asciidoctor?

I'm using asciidoctor to generate a static site for an upcoming event in my hometown, and we have a list of hotels and places of interest for out of town guests. Each location has a position on the map, a type (hotel, restaurant, ect.) and whether their a sponsor of the event.
I've read the docs on Block Processors and this seems to be the best way to do it, but I can't figure out how to tag a title with metadata that can be processed this way.
Currently, we have the descriptions of each location in the document like this
== Locations
[[Fancy_Hotel]]
=== Fancy Hotel
[location, 55, 73, hotel, sponsor]
A nice hotel in the middle of town
[[Chain_Restaurant]]
=== Chain Breakfast Restaurant
[location, 98, 16, restaurant]
A good place for food
and I read the metadata when building the page, then populate the map with clickable links which navigate to the appropriate entry. The following code works, but I'd like to move the metadata above the title if possible, so I'm not relying on parent so aggressively.
require 'asciidoctor'
require 'asciidoctor/extensions'
$Locations = Array.new
def make_map_svg locs
# Do some magic here
locs.each {|a| puts a}
end
class LocationBlock < Asciidoctor::Extensions::BlockProcessor
use_dsl
named :location
on_context :paragraph
name_positional_attributes ['x', 'y', 'type', 'sponsor']
def process parent, reader, attrs
x = ((attrs.delete 'x') || 0).to_i
y = ((attrs.delete 'y') || 0).to_i
type = attrs.delete 'type'
sponsor = (attrs.delete 'sponsor') == 'sponsor'
$Locations.push({
x:x,
y:y,
type:type,
sponsor:sponsor,
title:parent.title,
parent.id
})
create_paragraph parent, reader.lines, attrs, {}
end
end
Asciidoctor::Extensions.register do
block LocationBlock
end
Asciidoctor.convert_file "./Locations.adoc"
make_map_svg $Locations
The make_map_svg function works fine, the issue is figuring out how to make the metadata entries less hacky.
You might think of the location data as metadata, but processing it with a block processor implies that it is not metadata; blocks occupy a location in Asciidoctor's AST and are expected to occupy a position in the output; there should be something rendered at that location.
Titles are special in that they define both the text of the section/document title, but also a container for the title and the section's child elements. If you use a block processor, it must follow the title, or it is not enclosed in the container with the title and other child elements. Navigating the element structure from your [location ...] block to the entitled section would be more challenging than just using parent.
If the location data is truly metadata, in that you do not expect anything to render where [location ...] exists, you might consider implementing your logic as a preprocessor, which could read the location information from a comment in the lines of your source.
One advantage of using the preprocessor approach to parse comments is that anyone using your Asciidoctor source would see the same rendering (aside from CSS), whether they had your extension installed/active or not.

How to check if all element of a list is inside of a list of strings

Im parsing a website to catch available products and there sizes. Theres 3 products loaded. Theres a list named 'find_id_1' that houses 3 elements, each element has the product name and their variant ids. I made 2 other list one named keywords and one named negative. the keywords list houses the keywords that my desired product title should have. If any elements from the negative list are in the product title then I don't want that product.
found_product = []
keywords = ['YEEZY','BOOST','700']
negative = ['INFANTS','KIDS']
find_id_1 = ['{"id":2069103968384,"title":
"\nYEEZY BOOST 700 V2","handle":**"yeezy-boost-700-v2-vanta-june-6"**,
[{"id":19434310238336,"parent_id":2069103968384,"available":true,
"sku":"193093889925","featured_image":null,"public_title":null,
"requires_shipping":true,"price":30000,"options"',
'{"id":2069103935616,"title":"\nYEEZY BOOST 700 V2 KIDS","handle":
"yeezy-boost-700-v2-vanta-kids-june-6",`
["10.5k"],"option1":"10.5k","option2":"",
`"option3":"","option4":""},{"id":19434309845120,"parent_id":2069103935616,
"available":false,"sku":"193093893625","featured_image":null,
"public_title":null,"requires_shipping":true,"price":18000,"options"',
'{"id":2069104001152,"title":"\nYEEZY BOOST 700 V2 INFANTS",
"handle":**"yeezy-boost-700-v2-vanta-infants-june-6"***,`
["4K"],"option1":"4k","option2":"",`
"option3":"","option4":""},{"id":161803398876,"parent_id":2069104001152,
"available":false,"sku":"193093893724",
"featured_image":null,"public_title":null,
"requires_shipping":true,"price":15000,"options"']
I've tried using a for loop to iterate through every element in find_info_1 then creating another for loop that iterates through every element in keyword and negative but i get the wrong product. Heres my code:
for product in find_id_1:
for key in keywords:
for neg in negative:
if key in product:
if neg not in product:
found_product = product
It prints the following:
'{"id":2069104001152,"title":"\nYEEZY BOOST 700 V2 INFANTS",
"handle":"yeezy-boost-700-v2-vanta-infants-june-6,`
["4K"],"option1":"4k","option2":"",`
"option3":"","option4":""},
{"id":161803398876,"parent_id":2069104001152,
"available":false,"sku":"193093893724",
"featured_image":null,"public_title":null,
"requires_shipping":true,"price":15000,"options"']
Im trying to get it to return element 0 from find_info_1 because thats the only one that doesn't have any of the elements from the list negative. Would using a for loop be the best and fastest way to iterate through my list? Thank you! Any help is welcome!
First of all you should'nt treat a json data as a string. Just parse the json using json library so you can check just the title of the product. As the product list and the specification of each of the product get bigger, the time taken for iteration increases.
To answer your question, you can simply do
for product in find_id_1:
if any(key in product for key in keywords):
if not any(neg in product for neg in negative):
found_product.append(product)
this will get you the element as per your specification. however I made some changes to your data, just to make it a valid python code..
found_product = []
keywords = ['YEEZY','BOOST','700']
negative = ['INFANTS','KIDS']
find_id_1 = [""""'{"id":2069103968384,"title":
"\nYEEZY BOOST 700 V2","handle":**"yeezy-boost-700-v2-vanta-june-6"**,
[{"id":19434310238336,"parent_id":2069103968384,"available":true,
"sku":"193093889925","featured_image":null,"public_title":null,
"requires_shipping":true,"price":30000,"options"'""",
""""'{"id":2069103935616,"title":"\nYEEZY BOOST 700 V2 KIDS","handle":
"yeezy-boost-700-v2-vanta-kids-june-6",`
["10.5k"],"option1":"10.5k","option2":"",
`"option3":"","option4":""},{"id":19434309845120,"parent_id":2069103935616,
"available":false,"sku":"193093893625","featured_image":null,
"public_title":null,"requires_shipping":true,"price":18000,"options"'""",
""""'{"id":2069104001152,"title":"\nYEEZY BOOST 700 V2 INFANTS",
"handle":**"yeezy-boost-700-v2-vanta-infants-june-6"***,`
["4K"],"option1":"4k","option2":"",`
"option3":"","option4":""},{"id":161803398876,"parent_id":2069104001152,
"available":false,"sku":"193093893724",
"featured_image":null,"public_title":null,
"requires_shipping":true,"price":15000,"options"'"""]
for product in find_id_1:
if any(key in product for key in keywords):
if not any(neg in product for neg in negative):
found_product.append(product)
print(found_product)

Insert values into API request dynamically?

I have an API request I'm writing to query OpenWeatherMap's API to get weather data. I am using a city_id number to submit a request for a unique place in the world. A successful API query looks like this:
r = requests.get('http://api.openweathermap.org/data/2.5/group?APPID=333de4e909a5ffe9bfa46f0f89cad105&id=4456703&units=imperial')
The key part of this is 4456703, which is a unique city_ID
I want the user to choose a few cities, which then I'll look through a JSON file for the city_ID, then supply the city_ID to the API request.
I can add multiple city_ID's by hard coding. I can also add city_IDs as variables. But what I can't figure out is if users choose a random number of cities (could be up to 20), how can I insert this into the API request. I've tried adding lists and tuples via several iterations of something like...
#assume the user already chose 3 cities, city_ids are below
city_ids = [763942, 539671, 334596]
r = requests.get(f'http://api.openweathermap.org/data/2.5/groupAPPID=333de4e909a5ffe9bfa46f0f89cad105&id={city_ids}&units=imperial')
Maybe a list is not the right data type to use?
Successful code would look something like...
r = requests.get(f'http://api.openweathermap.org/data/2.5/group?APPID=333de4e909a5ffe9bfa46f0f89cad105&id={city_id1},{city_id2},{city_id3}&units=imperial')
Except, as I stated previously, the user could choose 3 cities or 10 so that part would have to be updated dynamically.
you can use some string methods and list comprehensions to append all the variables of a list to single string and format that to the API string as following:
city_ids_list = [763942, 539671, 334596]
city_ids_string = ','.join([str(city) for city in city_ids_list]) # Would output "763942,539671,334596"
r = requests.get('http://api.openweathermap.org/data/2.5/group?APPID=333de4e909a5ffe9bfa46f0f89cad105&id={city_ids}&units=imperial'.format(city_ids=city_ids_string))
hope it helps,
good luck

dict comprehension with list as value

I have a program that prompts the user for three pieces of input:
Student No.
Name
Age
I then populate a list with this info. This currently works fine.
I was wanting to change the list construct to a dictionary that has the Student No. as the key and a list with the Name and Age as values. I also want the list in case I decide to enlarge the number of inputs in the future.
I was wondering if I could use a dict comprehension but I can't find any examples of using a dict comp being used to create a dict with a list as the value.
The resulting dictionary would look like:
st_dict = {101: [Mark, 54],
102: [Bob, 49]
}
Is it possible, using a dict comp, to create this structure?
Basically I am wanting to do this:
st_dict = {Student_no: [Name, Age] per student entered}

Resources