Max
Max

Reputation: 15

How to delete one json item when each object has the same key name

I'm creating a simple, personal library project with python. I have code that when somebody borrows a book, it will place the book into a json file with their id/library card number like this:

customers.json

{
    "customers": [
        {
            "id": "1234",
            "book": "Ulysses",
            "book2": "War and Peace",
            "book3": "Memoirs of Hadrian",
            "book5": "Season of Migration to the North"
        },
        {
            "id": "4321",
            "book": "Mrs Dalloway",
            "book2": "Great Expectations"
        },
        {
            "id": "1973",
            "book": "The Aeneid"
        }
    ]
}

This code takes the user's input and loops through each of the values to find the corresponding key.

return_book_input = input('What book do you want to return: ')

with open ('customers.json') as f:  
    customer_data = json.load(f)

    values = []
    keys = []
    def get_key():
      for customer in customer_data['customers']:
        for key, value in customer.items(): 
          if return_book_input == value:
            keys.append(key)
            values.append(value)
            
      print(keys, values)
    
  write_json(customer_data, 'customers.json')

  get_key()

Sample output:

['book2'] ['War and Peace']

Question: How do I delete this key value pair from the customer with the id: 1234? I want to delete the key value pair with the key: 'book2' and value: 'War and Peace'. Also, is there a way to delete a dictionary key value pair based on value instead of key?

My goal isn't to get the fastest code but the easiest to write and read.

Upvotes: 1

Views: 66

Answers (1)

pho
pho

Reputation: 25500

You already made a list containing the keys you want to delete. Now, move its declaration to inside the for customer in customer_data loop, because you want to delete a fresh set of keys for every customer.

with open ('customers.json') as f:  
    customer_data = json.load(f)


return_book_input = "Ulysses"

for customer in customer_data['customers']:
    keys_to_delete = []
    for key, value in customer.items(): 
      if return_book_input == value:
        keys_to_delete.append(key)
    
    for key in keys_to_delete:
        del customer[key]

print(customer_data)

Now, the customer_data dictionary doesn't contain the book1 key for the first customer.

{
    "customers": [
        {
            "id": "1234",
            "book2": "War and Peace",
            "book3": "Memoirs of Hadrian",
            "book5": "Season of Migration to the North"
        },
        {
            "id": "4321",
            "book": "Mrs Dalloway",
            "book2": "Great Expectations"
        },
        {
            "id": "1973",
            "book": "The Aeneid"
        }
    ]
}

This doesn't seem like a great way to structure your data, though. If you have the ability to change it, I strongly suggest you do. Each customer dictionary will have a key "id" for their customer id, and a key "books" for the books they borrowed. The value of the books key will be a list. This way, your object structure doesn't change when a customer borrows multiple books -- you simply add more entries to the list.

customers.json:

{
    "customers": [
        {
            "id": "1234",
            "books": ["Ulysses", "War and Peace", "Memoirs of Hadrian", "Season of Migration to the North"]
        },
        {
            "id": "4321",
            "books": ["Mrs Dalloway", "Great Expectations"]
        },
        {
            "id": "1973",
            "books": ["The Aeneid"]
        }
    ]
}

Then, you'd use list.remove() to remove the book -- no explicit iterating required, list.remove() handles it for you. If the book doesn't exist in the list, a ValueError is thrown that we can catch to detect when this happens:

with open ('customers.json') as f:  
    customer_data = json.load(f)

for customer in customer_data["customers"]:
    try:
        customer["books"].remove(return_book_input)
        print(f"Thank you for returning {return_book_input}, Customer {customer['id']}")
    except ValueError:
        print(f"Customer {customer['id']} hasn't borrowed the book {return_book_input}")

This gives the output:

Thank you for returning Ulysses, Customer 1234
Customer 4321 hasn't borrowed the book Ulysses
Customer 1973 hasn't borrowed the book Ulysses

And once we're done, customer_data is:

{
    "customers": [
        {
            "id": "1234",
            "books": [ "War and Peace", "Memoirs of Hadrian", "Season of Migration to the North" ]
        },
        {
            "id": "4321",
            "books": [ "Mrs Dalloway", "Great Expectations" ]
        },
        {
            "id": "1973",
            "books": [ "The Aeneid" ]
        }
    ]
}

In fact, you don't even need to iterate over the customers! Since you probably know your customer's ID already (it's presumably on their library card), customer_data["customers"] could be a dict. The keys of this dict are the customer ID. The values are the dictionaries for each customer, same as before.

customer.json:

{
    "customers": {
        "1234": {
            "id": "1234",
            "books": [ "War and Peace", "Memoirs of Hadrian", "Season of Migration to the North" ]
        },
        "4321": {
            "id": "4321",
            "books": [ "Mrs Dalloway", "Great Expectations" ]
        },
        "1973": {
            "id": "1973",
            "books": [ "The Aeneid" ]
        }
    }
}

Then, after reading the json you'd simply do:

customer_id = "1234" # input("Enter Customer ID: ")
return_book_input = "Ulysses" # input("Enter book: ")

try:
    # Find the correct customer
    customer = customer_data["customers"][customer_id] # If fail throws KeyError

    # Remove book
    customer["books"].remove(return_book_input) # If fail throws ValueError

    print(f"Thank you for returning {return_book_input}, Customer {customer['id']}")
except KeyError: # Handle no customer
    print(f"Customer {customer_id} does not exist")
except ValueError: # Handle no book
    print(f"Customer {customer['id']} hasn't borrowed the book {return_book_input}")

Upvotes: 2

Related Questions