# Yhteystiedot ja osoitekirja 3/3 - Yhteystietojen järjestäminen aakkosjärjestykseen Tässä tehtävässä sinun tehtäväsi on järjestää yhteystiedot nimien mukaiseen aakkosjärjestykseen. Tämä tehtävä on ohjelmointi 1 -kurssin näkökulmasta edistynyttä lisäsisältöä, minkä vuoksi se on julkaistu bonustehtävänä. ## Taustatietoa Jos yritämme järjestää oman Contact-luokan oliot Collections.sort-metodin avulla, saamme seuraavan virheilmoituksen: ``` "The method sort(List) in the type Collections is not applicable for the arguments (List)" ``` Tämä johtuu siitä, että luokkamme ei ole yhteensopiva `Comparable`-tyypin kanssa. `Collections.sort`-metodi ei siis pysty vertailemaan olioitamme keskenään ilman, että määrittelemme sille lisäksi käytettävän vertailuoperaation. Tutustu Javan `Comparator.comparing`-metodiin, jonka avulla voit määritellä vertailijan kutsumaan mitä tahansa oman luokkasi metodia olioiden järjestämiseksi: [https://www.baeldung.com/java-8-comparator-comparing](https://www.baeldung.com/java-8-comparator-comparing). Tällä kurssilla sinun kannattaa lukea artikkelista kohta [3.1. Key Selector Variant](https://www.baeldung.com/java-8-comparator-comparing#1-key-selector-variant) ja sitä aikaisemmat kappaleet, mutta ei välttämättä tätä kappaletta pidemmälle. Kuten artikkelissa kerrotaan, `Comparator.comparing`-metodille voidaan antaa metodiviittaus mihin tahansa metodiin. `Collections.sort` käyttää tällöin vertailussa juuri haluamasi metodin palauttamia arvoja, joita vertaillaan keskenään. Voisimme siis esimerkiksi järjestää merkkijonot pituusjärjestykseen vertailemalla merkkijonojen pituuksia, jotka selviävät `length()`-metodin avulla: `Comparator.comparing(String::length)`. `Collections.sort` käyttää tässä esimerkissä järjestelemiseen `Comparator`-oliota, joka vertaa merkkijonojen pituuksia toisiinsa: ``` List nesteet = Arrays.asList("maito", "Vesi", "ketsuppi", "bensa", "Limu"); // tehdään järjestely merkkijonojen length-metodin mukaan: Collections.sort(nesteet, Comparator.comparing(String::length)); System.out.println(nesteet); // lyhimmästä merkkijonosta pisimpään: [Limu, Vesi, bensa, maito, ketsuppi] ``` ## Tehtävä Jatkokehitä nyt edellisissä tehtävissä käyttämiäsi kolmea osoitekirja- ja yhteystietoluokkaa siten, että osoitekirjan sisältöä listattaessa "list"-komennolla yhteystiedot esitetään aakkosjärjestyksessä nimen mukaan. Sinun kannattaa hyödyntää edellä käsiteltyä `Collections.sort`-metodia sekä `Comparator.comparing`-metodia siten, että annat comparing-metodille metodiviittauksen `Contact`-luokkasi `getName`-metodiin. ## ESIMERKKI ``` This is an address book application. Available commands: list help add , , search exit > add Maija Meikäläinen, maija@example.com, +35850555556 Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556) > add Matti Meikäläinen, matti@example.com, +35850555555 Added Matti Meikäläinen (email: matti@example.com, phone: +35850555555) > add Benjamin Meikäläinen, benjamin@example.com, +35850555557 Added Benjamin Meikäläinen (email: benjamin@example.com, phone: +35850555557) > add Abraham Meikäläinen, abraham@example.com, +35850555558 Added Abraham Meikäläinen (email: abraham@example.com, phone: +35850555558) > add Wolverine Meikäläinen, wolverine@example.com, +35850555559 Added Wolverine Meikäläinen (email: wolverine@example.com, phone: +35850555559) > list Abraham Meikäläinen (email: abraham@example.com, phone: +35850555558) Benjamin Meikäläinen (email: benjamin@example.com, phone: +35850555557) Maija Meikäläinen (email: maija@example.com, phone: +35850555556) Matti Meikäläinen (email: matti@example.com, phone: +35850555555) Wolverine Meikäläinen (email: wolverine@example.com, phone: +35850555559) > exit Bye! ``` **AddressBookApp.java** ``` import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class AddressBookApp { private static String welcomeText = "This is an address book application. Available commands:\n"; private static String commandMenu = " list\n" + " help\n" + " add , , \n" + " search \n" + " exit\n"; // Keep here instead of translating to a local variable private static String inputCLIPrefix = "> "; // Keep added addressBook values during application runtime private static AddressBook addressBook = new AddressBook(); public static void main(String[] args) { System.out.print(welcomeText + commandMenu); while (true) { runApp(); } } private static void runApp() { Scanner inputPrompt = new Scanner(System.in); Object[] getInput = parseInput(inputCLIPrefix, inputPrompt); String inputCmd = String.valueOf(getInput[0]); // TODO Avoid risk of ClassCastException List inputArgs = (ArrayList)getInput[1]; int argsCount = 3; switch(inputCmd) { case "list": System.out.println(addressBook.toString()); break; case "help": System.out.print(welcomeText + commandMenu); break; case "add": // TODO Should this be checked in AddressBook.add method instead? if (inputArgs.size() != argsCount) { System.err.printf("Invalid count of input arguments (expected %d).\n", argsCount); break; } String name = String.valueOf(inputArgs.get(0)); String email = String.valueOf(inputArgs.get(1)); String phone = String.valueOf(inputArgs.get(2)); Contact contact = new Contact(name, email, phone); // NOTE: Else condition not needed as 3 arguments is always passed to this object method call if(addressBook.add(contact)) { System.out.printf("Added %s\n", contact.toString()); } break; case "search": String searchTerm = String.valueOf(inputArgs.get(0)); Contact match = addressBook.search(searchTerm); if (match == null) { System.out.printf("%s does not match any contact.\n", searchTerm); } else { System.out.println(match.toString()); } break; case "exit": System.out.print("Bye!\n"); System.exit(0); default: System.err.println("Command not found."); //break; } } private static Object[] parseInput(String prefix, Scanner inputRaw) { System.out.print(prefix); String inputR = inputRaw.nextLine(); String command = inputR.split(" ")[0]; String[] theRest = inputR.split(","); theRest[0] = theRest[0].replaceAll("^\\s*" + command + "\\s*",""); List arguments = new ArrayList(); for (int i = 0; i < theRest.length; i++) { arguments.add(theRest[i].trim()); } Object[] parsedOutput = new Object[2]; parsedOutput[0] = command; parsedOutput[1] = arguments; return parsedOutput; } } ``` **AddressBook.java** ``` import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class AddressBook { private List contacts; public AddressBook() { this.contacts = new ArrayList<>(); } public boolean add(Contact newContact) { // Create a temporary ArrayList, if we have contacts already // Get list of all contacts, put them into the new ArrayList contactsTemp // Loop through all contacts in contactsTemp // Check the current contact in contactsTemp against newContact if (this.contacts.size() > 0) { List contactsTemp = new ArrayList<>(this.contacts); for (Contact tempContact : contactsTemp) { if (tempContact.equals(newContact)) { System.out.println("That contact already exists."); return false; } } } this.contacts.add(newContact); return true; } public Contact search(String keyword) { for (Contact current : this.contacts) { String name = current.getName(); if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) { return current; // palautetaan löytynyt arvo heti } } return null; // palautetaan null, jos ei löytynyt } @Override public String toString() { String returnString = ""; // Sort by contact names. Define new Comparator object Collections.sort(this.contacts, new Comparator() { public int compare(Contact contact1, Contact contact2) { return contact1.getName().compareTo(contact2.getName()); } }); for (Contact contact : this.contacts) { returnString += contact + "\n"; } return returnString; } } ``` **Contact.java** ``` public class Contact { private String name; private String email; private String phone; // NOTE: This does not validate or check input String values // (Is name actually a name, email a valid email, and phone number an actual phone number?) public Contact(String name, String email, String phone) { this.name = name; this.email = email; this.phone = phone; } public String getName() { return this.name; } @Override public boolean equals(Object contact) { if (this == contact) { return true; } if (!(this instanceof Contact)) { return false; } Contact contactT = (Contact) contact; if (this.name.equals(contactT.name) && this.email.equals(contactT.email) && this.phone.equals(contactT.phone)) { return true; } return false; } public String toString() { // Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)" return this.name + " (email: " + this.email + ", phone: " + this.phone + ")"; } } ```