Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby Build an Address Book in Ruby Input and Output Saving The Address Book

Erik Nuber
Erik Nuber
20,629 Points

Extra Credit: Delete a contact from the address book

Finished the extra credit. Will explain each area...

just added the Delete Contact Option in the menu area.

def run
    loop do
      puts "Address Book"
      puts "a: Add Contact"
      puts "d: Delete Contact"
      puts "p: Print Adddress Book"
      puts "s: search"
      puts "e: Exit"
      print "Enter your choice: "
      input = gets.chomp.downcase
      case input
        when 'e'
          save()
          break
        when 'p'
          print_contact_list
        when 's'
            print "Search term: "
            search = gets.chomp
            find_by_name(search)
            find_by_phone_number(search)
            find_by_address(search)
        when 'a'
          add_contact
        when 'd'
          delete_contact
      end
    end
  end

first we have the delete_contact method. It assigns an index to 0 which I use to keep track of the length of the array. It is used in another method and will get passed to it.

• first i check to see if the contacts are empty and if so, I let the user know and it will exit out of the menu

• next I check to see if the user would like to remove a contact based on first or last name and set that to a variable nameType

• then I find out what the name is to be removed and set it to the variable nameRemove

• a case statement based on the nameType variable is performed and, within the case statement there is an each loop that will either check by first or last name. To use DRY coding, I moved the code to be performed on each contact into another method. When the method check_name is finished I then increase the index which is also passed to the check_name method.

 def delete_contact
      index = 0
      if contacts.empty?
        puts "There are no contacts in your address book. \n"
      else
        print "Would you like to remove a contact based on first or last name? (first/last) "
        nameType = gets.chomp.downcase
        print "What is the name you would like to remove? "
        nameRemove = gets.chomp.downcase
            case nameType
              when "first"
                  contacts.each do |contact|
                    if contact.first_name.downcase.include?(nameRemove)
                      check_name(contact, index)
                      index += 1
                     end
                    end
              when "last"
                  contacts.each do |contact|
                    if contact.last_name.downcase.include?(nameRemove)
                       check_name(contact)
                      index += 1
                    end
                  end  
                else
                  puts "You did not enter first or last as a choice."
             end
        end
   end

The check_name method is designed to find out if the given contact is the correct contact to remove. As there can be several people with the same first or last name on the list, this will allow the correct contact to be removed rather than just the first one on the list.

So it starts by putting the contact name down, and then asks if it is the correct person to remove. the loop forces the user into a Y or N answer and, finally when that condition is met, it calls yet another method that will actually deal with deleting the name. It sends three arguments, the answer of Y or N, the contact in question and the index position within the array

  def check_name(contact, index)
   puts contact.to_s('last_first')
   loop do
   print "Is this the person you would like to remove? (Y/N) "
   answer = gets.chomp.downcase  
     if (answer == "y" || answer == "n")
        remove_name(answer, contact, index)
        break
     end  
   end  
  end

Finally the method that will actually remove the contact. If the answer is Y it simply deletes the contact. If the contact is N it puts some flavor text. However, that flavor text will only run if there are still names to be checked else, it just exits out to the main menu.

  def remove_name(answer, contact, index)
    case answer
      when "y"
        contacts.delete(contact)
      when "n"
        if (index < contacts.size - 1)
          puts "Okay, how about this one? "  
        end
    end      
  end  

Yes, I likely made this more complicated than necessary. It could have been shorted to two methods but, I expanded on the code for my own learning purposes. I want to reinforce the materials, send arguments all over etc. I also wanted to cover various cases such as no contacts to delete and, if there was more than one person sharing the same name.

This was not necessarily easy to accomplish. I used a lot of testing to see what was actually happening especially when what I thought what was supposed to happen didn't happen.

6 Answers

Nelly Nelly
Nelly Nelly
7,134 Points

Thank you very miuch I didn't find how to do, you did a GREAT job: Something went wrong for me with this code "wrong number of arguments"

def delete_contact
      index = 0
      if contacts.empty?
        puts "There are no contacts in your address book. \n"
      else
        print "Would you like to remove a contact based on first or last name? (first/last) "
        nameType = gets.chomp.downcase
        print "What is the name you would like to remove? "
        nameRemove = gets.chomp.downcase
            case nameType
              when "first"
                  contacts.each do |contact|
                    if contact.first_name.downcase.include?(nameRemove)
                      check_name(contact, index)
                      index += 1
                     end
                    end
              when "last"
                  contacts.each do |contact|
                    if contact.last_name.downcase.include?(nameRemove)
                       check_name(contact) #mistake was here
                      index += 1
                    end
                  end  
                else
                  puts "You did not enter first or last as a choice."
             end
        end
   end

I have added the "index" argument in this part :

          when "last"
                  contacts.each do |contact|
                    if contact.last_name.downcase.include?(nameRemove)
                       check_name(contact, index) #added it there :)
                      index += 1
                    end
                  end  
                else
                  puts "You did not enter first or last as a choice."
             end

Cheers :)

Melissa Ramirez
Melissa Ramirez
6,503 Points

For solving this extra credit challenger I created only this two methods:

def remove
    print "Write the name or last name to remove: "
    remove_title = gets.chomp
    delete_contact(remove_title)
end


def delete_contact(remove_title)
    contacts.each do |contact|
        if contact.full_name.downcase.include?(remove_title)
            contacts.delete(contact)
        end
    end
    save
end

Small improvement:

  def delete_contact
    index = 0
    if contacts.empty?
      puts "There are no contacts in your address book. \n"
    else
      print "Would you like to remove a contact based on first or last name? (first/last) "
      nameType = gets.chomp.downcase
      if (nameType == 'first' || nameType == 'last')
        print "What is the name you would like to remove? "
        nameRemove = gets.chomp.downcase
          case nameType
            when "first"
              contacts.each do |contact|
                if contact.first_name.downcase.include?(nameRemove)
                  check_name(contact, index)
                  index += 1
                end
              end
            when "last"
              contacts.each do |contact|
                if contact.last_name.downcase.include?(nameRemove)
                  check_name(contact, index)
                  index += 1
                end
              end
          end
      else
        puts "You did not enter first or last as a choice."
      end
    end
  end
Daniel Grieve
Daniel Grieve
6,432 Points

I added one method which should account for contacts with the same name, or contacts with similar names.

when 'r' results = [] puts "Enter a search term for the contact you wish to remove" search = gets.chomp.downcase contacts.each do |contact| if contact.first_name.downcase.include?(search) || contact.last_name.downcase.include?(search) results.push(contact) end end results.each do |contact| puts "Is this the contact you wish to delete: "
puts contact.full_name contact.print_phone_numbers puts "Delete contact? y/n" if gets.chomp == 'y' contacts.delete(contact) end end

Here is how I went about implementing mine, this allows a user to see all the contacts listed with serials even if the user does not pass a query for contact to be deleted. Where a first name or last name argument is provided and there are multiple contacts that match the search, the results are printed out and the user can select by serial number.

def delete_contact
    if contacts.empty?
      puts "There are no contacts in your address book"
    end

    loop do
      break if contacts.empty?
      puts "e: Exit"
      input = prompt("Enter contact first name or last name").downcase

      case input
      when 'e'
        break
      end

      results = contacts.select { |contact| contact.full_name.downcase.include?(input) }
      results.each_with_index {|contact, index| puts "#{index + 1}. #{contact.to_s('last_first')}"}
      input = prompt("Select contact index to delete (1 to delete first contact e.t.c)")

      if (1..results.length).include?(input.to_i)
        loop do
          puts "Enter any key to go back: "
          contact = results[input.to_i - 1]
          input = prompt("Delete #{contact.to_s('last_first')}! Are you sure? (y)").downcase
          case input
          when 'y'
            @contacts.delete(contact)
            puts "#{contact.to_s('last_first')} deleted!"
            break
          else
            puts "Operation aborted!"
            break
          end
        end
      end
    end
  end

Another caveat is what happens when you delete the last contact in the address book which is why I decided to break the loop after every run if the contacts list is empty