20 Nov 2020
Maintaining a streamlined process to draft, edit, and publish technical articles can be tricky. For example, I write across multiple topics such as Python tutorials, bash commands, and Ruby optimizations. I also publish to three different locations—my website, dev.to, and Hashnode, so I write my posts in Markdown-friendly tools for cross compatibility.
Notion is a great tool for drafting blog posts, technical or otherwise. Notion has an entire suite of tools to make drafting easy: Markdown exports, page properties and filters, a beautiful interface, and many more. With Notion, I quickly draft articles, publish each one across multiple sites, and monitor each article’s progress at a glance. Here’s what my setup looks like:

In this article, we will walk through creating an Articles page in Notion. We’ll create:
- A page for hold articles
- An articles database
- A new draft template, and
- A Kanban board to display each draft’s status
Markdown, Jekyll, and Drafts! Oh My!
For a while, I kept my drafts in a _drafts
folder in my website’s repo. Jekyll, my static site generator of choice, ignores any Markdown files in the _drafts
folder. However, I’ve been hesitant to heavily draft and edit posts in a publicly available location, so I haven’t been drafting many posts lately. I love that my website is open source for others to use as an example for static site layouts, but I would like to keep the messiness of article writing to myself.
Then, I tried drafting my articles in the app Drafts. Using Drafts, I could write posts in Markdown and preview them privately, but the only way to mark the status of a post (draft, edit, published, abandoned, etc.) was via tags. I used tags like blog
and draft
for quick filtering:

Unfortunately, tags didn’t scale for me. The only way to view each draft is from the small sidebar on the left, and I had to remember what combination of tags I used to mark blog drafts. Often, I’d forget to tag posts, or I’d forget to update the tags after I had drafted it. I also wanted to have a space devoted only to my articles so that I could get a bird’s eye view of what I had in the pipeline. That’s where Notion databases come to the rescue.
The Notion Database
Databases in Notion let me organize and structure related pages. Now that my articles are in a database, I can label, search, and create views of articles. For example, I can look at all of my articles as a list:

I can also view article drafts as a gallery, with a preview of each article:

I can even view my articles as a Kanban board based on their status. I use this birds eye view to see what articles are currently being drafted, which are in review, and which have been posted or abandoned:

Creating the Table
Let’s start by creating a page and a database to hold the articles. First, click “+ Add a page” on the left sidebar. Give the page any title, icon, and header that you want. I titled mine “My Articles”:

Next, we need to create an article database. Click the text that says “Type ‘/’ for commands”, then type /list inline
. Choose the option labelled “List - Inline” under “Database”:

Notion will create a new list for you and populate a few example pages:

Add a title to your table by clicking on “Untitled” and adding a title, such as “Articles”:

Table Properties
Let’s add some article properties for our pages. In this database, each article will be a page in Notion. Any properties we add to the Articles database will be shown in each page. Each article needs a title, creation date, last edited time, and status. For articles, the title property will be the the title of the Notion page, but we need to add the rest of the properties to the table.
To add properties, hover over the Articles table, click on the three dots on the right, and select “Properties”. Add a status property by clicking “+ Add a property”. Then, for “Property Type”, pick “Select”. To show the property in the table view, enable the toggle next to Select. Similarly, add a “Created” property by selecting the type “Created time”, and add an “Edited” property by selecting the type “Last edited time”. Delete the “Tags” property by selecting the dots on the left, and then select “Delete”. The Articles table properties now look like this:

Now that our table is set up for articles, we can create a “New Draft” page template!
The Page is as Mighty as the Table
Most likely, you’ll add new pages to your Articles table when you’re creating a new draft. With a “New Draft” page template, it’s easy to start drafting and not need to worry about setting any page properties. To create a new draft, click the dropdown arrow next to “New” on the Articles table, and then click “+ New template”.
Name the template “New Draft”. We’ll want any new draft to have a Draft status, so we need to create that status. Click the text “Empty” next to the Status property, type in “Draft”. This will create the Draft status for any Article page, and assign the Draft status to any page that is created with the New Draft template. I also added a ✏️ icon to the New Draft template and the Draft that they’re easy to find in lists:

Hello World
Let’s write our first article draft! Click the “Back” button to go back to the Articles table, then select “New Draft” to begin writing. Add a title and some example text for your first draft:

Click “Back” again to go to the Articles table, and delete the old example pages. Practice using Notion page templates by adding another draft:

You are Your Own Publisher
One way to publish your articles is to make the page publicly accessible in Notion. You can also use Notion’s Markdown exporter to convert a page into Markdown and then import the content into sites such as dev.to or Hashnode. To export a page to Markdown, click the three dots in the top right of a page, select “Export”, then select “Markdown & CSV”. The page contents will be converted into Markdown for cross compatibility:

Great, Now There’s Two of Them
You’ve finished editing and publishing your “Hello, World!” article. Now it’s time to change the status of that article to Posted, but we don’t have an easy way to do that yet. Let’s create a new Kanban board view of our Articles table. With a Kanban board, you can move articles as cards from one status to the next.
To create a new table view, click “+ Add a view” at the top of the Articles table. Select the “Board” option and then click “Create”. Notion will automatically set up the Board view to sort articles by the Status property:

To add the Posted status, click “+ Add a group” and enter “Posted”. No pages have the Posted status yet, so the column will be empty:

To mark the “Hello, World!” article as posted, simply drag that article’s card from Draft to Posted, and it’s done!

With Notion’s databases, page properties, and custom views, you can create your own system to keep track of your blog posts and articles. I’m looking forward to using Notion more to structure my data in different aspects of my life.
17 Nov 2020
If you were designing a contacts app, how would you quickly access a phone number for a person given their name? You could keep an array of Contact
objects and check each name:
contacts = [ ... ]
def find_contact(name):
for person in contacts:
if person.full_name == name:
return person
return None
This won’t scale very well if you have a lot of contacts though. What if your user has thousands of contacts on their phone? Instead, you could create a database table, index by name, and use SQL to find the contact:
db_connection = connect_to_db(...)
def find_contact(name):
query = db_connection.sql(f'select * from contacts where name like {name}')
return query.first_or_none()
Databases are great for this! But what if you’re keeping a list of contacts locally for a short period of time? Maybe you don’t want the hassle of maintaining a database for a small project, or you’re designing an algorithm designed to solve a problem really fast. You can use a hash map!
What is a Hash Map?
A hash map associates a key
with a value
. Think of a phone book, or, if you’ve never seen a phone book, think of the Contacts app on your phone. If you want to know someone’s number, you look through your contact names, select the name you want, and that person’s phone number is shown next to their name:
Johnson, Sally 317-111-1111
Whittler, Mica 431-222-2222
Rodrigo, John 228-333-3333
For a hash map to hold a list of contacts, the key
could be a name, and the value
could be a phone number. A key
and a value
together are called a key value pair.

Typically, a key is a primitive data type like an integer, float, or string. Values can be any type, such as a string, number, list, or object. Hash maps are called dictionaries
(or dicts
) in Python, and can be used in many contexts. Here’s what a contacts dict
might look like:
contacts = {
"Sally Johnson": "317-111-1111",
"Mica Whittler": "317-222-2222",
"John Rodrigo": "318-333-3333"
}
# Prints "317-111-1111"
print(contacts["Sally Johnson"])
A hash map has three main parts:
- An array to hold items
- A hash function to calculate the index of an item for the array, and
- A method to handle hash collisions in the array (more on this later)
Let’s walk through an example: Cave Johnson’s phone number is 888-724-3623 (888-SCIENCE). In our contacts hash map, we want to insert the value 888-724-3623
with the key Cave Johnson
. Here’s what the Python code might look like:
# Create an empty dictionary (hash map)
contacts = {}
# Add the new contact
contacts["Cave Johnson"] = "888-724-3623"
# Prints '{"Cave Johnson": "888-724-3623"}'
print(contacts)
# Prints "888-724-3623"
print(contacts["Cave Johnson"])
Behind the scenes, the contacts
dictionary will compute the hash, or index, of the key Cave Johnson
, and insert the key value pair ("Cave Johnson", "888-724-3623")
at that index:

How can I build a Hash Map?
Let’s review the three main pieces we need to build a hash map:
- An array to hold key value pairs
- A hash function to generate indices for each key value pair, and
- A data structure inside the array to handle hash collisions
Generally, a hash map’s hash function will generate a unique index for any item inserted into the hash map. However, a hash function might return the same index for two different values, which is called a hash collision or index collision.

Hash maps must account for multiple key value pairs pointing to the same index. One way to account for index collisions is to create a linked list for each for each item in the main array. If two key value pairs are hashed to the same array index, the two pairs will be added to a linked list at that index:

Wrap Up
Hash maps are efficient data structures to associate keys
with values
. Hash maps use an array to hold key value pairs, a hash function to calculate the array index for each key value pair, and some data structure to account for index collisions.
To account for hash collisions in this series, each value of the hash map’s array will be a linked list. If any new key value pair has the same index as a previous pair, then the new pair will be added to the end of that index’s list. In the next post, we’ll learn how to build the linked list that we’ll use in our hash map class.
29 Sep 2020
One of the most common blocks you will write in Python scripts is a for loop. With for loops, you can repeat the same set of instructions in a block over and over. Python’s for loops are really foreach loops, where you repeat the instructions for every item in a collection. These collections are called iterators, which is something that a Python loop is able to iterate over, and the most common iterator is list
.
For Loops
Let’s look at an example of a for loop. Write a function that prints the square of each number from one to n
:
def write_squares(n):
for number in range(n):
square = number ** 2
print(square)
write_squares(6)
This is the output you will get from the above example:
In write_squares
, we calculate the variable square
and print it for each value in range(n)
, which is all of the numbers from 0
to n
inclusive.

Small for loops like this are very common in Python scripts. For example, we can read in lines from a file and strip any spaces from each line:
def lines_from(filename):
with open(filename, "r") as file_obj:
original_lines = file_obj.readlines()
stripped_lines = []
for line in original_lines:
new_line = line.strip()
stripped_lines.append(new_line)
Or, we might have a list of IDs and want to grab a piece of data for each ID:
def email_addresses(ids):
addresses = []
for identifier in ids:
data = api_request(identifier)
email_address = data["email"]
addresses.append(email_addresses)
With each of these for loops, we’re running the same piece of code on each item in the list. While the repeated piece of code isn’t too complicated, you still need to write (or read!) multiple lines to understand what the code block is doing. List comprehensions are an elegant way to create lists from existing lists.
List Comprehension
First, let’s look at a quick example:
chars = [letter for letter in "Hello, world!"]
print(chars)
When you run this Python code, the output will be:
['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
In this example, a new list is created, named chars
, that contains each of the items in the string "Hello, world!"
. Because items in a string are characters, chars
is a list of every character in the string.
List Comprehension Syntax
List comprehensions are written as:
new_list = [expression for item in old_list]
This is what a list comprehension “unwrapped” into a traditional list would look like:
new_list = []
for item in old_list:
new_item = expression
new_list.append(new_item)
Used correctly, list comprehensions can reduce for loops into a more readable, one line expression.

Examples
Let’s re-write our earlier examples using list comprehensions. Instead of writing an whole new function, our write_squares
example can be reduced to one line:
squares = [number ** 2 for number in range(6)]
print(squares)
We get the same results when we run this code:
Now let’s look at the line stripping function. We need to keep the first few lines to read the file contents, but the for loop that appended each stripped line to a new variable has been updated to use a list comprehension instead:
def lines_from(filename):
with open(filename, "r") as file_obj:
original_lines = file_obj.readlines()
return [line.strip() for line in original_lines]
The email address fetcher can be reduced to one line, and it can be directly placed in another block of code:
def email_addresses(ids):
return [api_request(id)["email"] for id in ids]
Conditionals
List comprehensions can also use conditional statements to filter or modify the values that are added to a new list. For example, if we wanted a list of only even integers from 1 to 20, we could add a conditional to the end of a list comprehension:
>>> evens = [num for num in range(0, 20) if num % 2 == 0]
>>> print(evens)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
List comprehensions can use any number of and
and or
operators in conditionals. For example, we can use the conditional (num % 2 == 0) and (num % 3 == 0)
to keep only numbers that are divisible by both 2 and 3:
>>> my_nums = [num for num in range(0, 20) if (num % 2 == 0) and (num % 3 == 0)]
>>> print(my_nums)
[0, 6, 12, 18]
Key Points
List comprehension is an elegant way to create new lists from existing lists. List comprehensions can reduce multiple-line code blocks to just one line! However, avoid writing large list comprehensions, as that may reduce legibility for your code readers.
Once you’re comfortable with list comprehensions, I recommend learning dictionary comprehensions, which are very similar to list comprehensions except that they operate on dictionaries.