HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Processing names and meanings from .txt files

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
frommeaningsnamesfilesandprocessingtxt

Problem

I wrote this program to process the names and meanings from .txt files. From the while loops I wrote, I noticed they are very similar loops containing Scanners. I am wondering if there is any way to reduce the redundancy in the code.
Also, for this program my instructor only allows us to use Scanners, while and if/else loops, boolean and file input to solve this problem.

Here are samples for names.txt and meanings.txt:

names.txt

Jax M 0 0 0 0 0 0 0 0 0 0 0 0 347

Randall M 974 620 650 467 369 232 70 59 100 136 192 440 841

Minnie F 8 22 50 87 122 168 258 516 995 0 0 0 0

Brianna F 0 0 0 0 0 0 0 0 0 340 61 15 30

meanings.txt

MUBINA f Arabic Feminine form of MUBIN

ANDRONIKOS m Ancient Greek Means "victory of a man" from Greek

(andros) "of a man" and (nike) "victory".

SALVATORE m Italian Italian cognate of SALVADOR

And here is my code:

```
import java.io.*;
import java.util.*;

public class BabyNames {
public static void main(String[] args) throws FileNotFoundException {
Scanner inputName = new Scanner(new File("names.txt"));
Scanner inputMeanings = new Scanner(new File("meanings.txt"));
Scanner console = new Scanner(System.in);

intro();
System.out.print("Name: ");
String nameIn = console.next();
System.out.print("Gender (M or F): ");
String genderIn = console.next();

nameAndMeaning(inputName, inputMeanings, nameIn, genderIn);
}

public static void intro() {
System.out.println("This program allows you to search through the");
System.out.println("data from the Social Security Administration");
System.out.println("to see how popular a particular name has been");
System.out.println("since 1890.");
System.out.println();
}

public static void nameAndMeaning(Scanner inputName, Scanner inputMeanings, String nameIn, String genderIn) {
boolean hasLine = false;
while(inputName.hasNextLine()) {
S

Solution

I would start from the declaration of main. You are throwing an exception from it but no other class will handle it. You should put your Scanners inside a try-catch and handle the exception properly.

I would also recommend reading everything from the files at once and use that collection of data in your code logic. A Scanner locks the file until it's finished with it.

Furthermore, you don't have to call System.out.println(...) just for the sake printing a new line. Use the \n escape sequence for doing the same

System.out.println("This program allows you to search through the \ndata from the Social Security Administration \nto see how popular a particular name has been \nsince 1890.\n");


The rest is just a search function to find the meaning based on the name and gender. I recommend creating a name Baby class with name and gender attributes and use a Hashtable to search for the meaning. The class can be as simple as

class Baby{
    private String name;
    public String Name(){
        return name;
    }

    private boolean gender;
    public boolean Gender(){
        return gender;
    }

    public Baby(String name, boolean gender){
        this.name = name;
        this.gender = gender;
    }
}


The time complexity of searching a value from a Hashtable is close to O(1) while a typical looping mechanism is O(n). The speed becomes n times slower as the size of the collection increases.

You also have a lot of duplicate code in the two while statements. To resolve that I'd recommend extracting out the if-else logic outside and calling a common function with a single loop. Something like

bool foundMatch = findMatch(...);
if(foundMatch) {
  findMatch(...) // with different params now
}
else {
  // print error message
}


Solving with Constraints

Given the constraints for the solution, I'd solve it as below

public static void main(String[] args){
    Scanner consoleInput = new Scanner(System.in);

    // Welcome message
    System.out.println("This program allows you to search through the \ndata from the Social Security Administration \nto see how popular a particular name has been \nsince 1890.\n");

    // Receive user input
    System.out.print("Name: ");
    String inputName = consoleInput.next();
    System.out.print("Gender: ");
    String inputGender = consoleInput.next();

    consoleInput.close(); // Closed as not needed anymore

    try (Scanner namesScanner = new Scanner(new File("names.txt"));
            Scanner meaningsScanner = new Scanner(new File("meanings.txt"))) {

        String foundMatch = findMatch(inputName, inputGender, namesScanner);
        if(!foundMatch.equals("")){
            System.out.println(foundMatch);
            System.out.println(findMatch(inputName, inputGender, meaningsScanner));
        } else{
            System.out.println("\""+ inputName + "\" not found.");
        }

    } catch (FileNotFoundException ex){
        System.out.println("File Not Found");
    }
}

private static String findMatch(String name, String gender, Scanner fileScanner){
    while(fileScanner.hasNextLine()){
        String currentLine = fileScanner.nextLine();

        // Split it to a String array and check the first two values
        String contents[] = currentLine.split(" ");
        if(name.equalsIgnoreCase(contents[0]) && gender.equalsIgnoreCase(contents[1])){
            return currentLine;
        }
    }
    // if no returns occured till the end of the file
    return "";
}


Please note that

  • The Scanners should be closed once you're done using them. Using Try-with-resources statement is a better alernative (as recommended in the comments). You don't have to worry about the Scanners being closed. The try block functions like a using from C# and releases all resources after the block itself is finished. The defined resources must implement the java.io.Closeable interface for this functionality and the Java project must use Java SE 7 or above.



  • Both name matching operations can be handled by the same function. The significance of positive matches is handled by the calling function. This also enforces Single Responsibility Principle (SRP) and improves code maintainability



  • Use equalsIgnoreCase(...) instead of equals() to avoid using toLowerCase() on all strings



  • return is called immediately from the function on the first match to avoid redundant checks



Now if you'll be very strict and won't accept the use of a String array. You can replace it by extracting the first and second values using substring(...) function on the first two whitespaces

Code Snippets

System.out.println("This program allows you to search through the \ndata from the Social Security Administration \nto see how popular a particular name has been \nsince 1890.\n");
class Baby{
    private String name;
    public String Name(){
        return name;
    }

    private boolean gender;
    public boolean Gender(){
        return gender;
    }

    public Baby(String name, boolean gender){
        this.name = name;
        this.gender = gender;
    }
}
bool foundMatch = findMatch(...);
if(foundMatch) {
  findMatch(...) // with different params now
}
else {
  // print error message
}
public static void main(String[] args){
    Scanner consoleInput = new Scanner(System.in);

    // Welcome message
    System.out.println("This program allows you to search through the \ndata from the Social Security Administration \nto see how popular a particular name has been \nsince 1890.\n");

    // Receive user input
    System.out.print("Name: ");
    String inputName = consoleInput.next();
    System.out.print("Gender: ");
    String inputGender = consoleInput.next();

    consoleInput.close(); // Closed as not needed anymore

    try (Scanner namesScanner = new Scanner(new File("names.txt"));
            Scanner meaningsScanner = new Scanner(new File("meanings.txt"))) {

        String foundMatch = findMatch(inputName, inputGender, namesScanner);
        if(!foundMatch.equals("")){
            System.out.println(foundMatch);
            System.out.println(findMatch(inputName, inputGender, meaningsScanner));
        } else{
            System.out.println("\""+ inputName + "\" not found.");
        }

    } catch (FileNotFoundException ex){
        System.out.println("File Not Found");
    }
}

private static String findMatch(String name, String gender, Scanner fileScanner){
    while(fileScanner.hasNextLine()){
        String currentLine = fileScanner.nextLine();

        // Split it to a String array and check the first two values
        String contents[] = currentLine.split(" ");
        if(name.equalsIgnoreCase(contents[0]) && gender.equalsIgnoreCase(contents[1])){
            return currentLine;
        }
    }
    // if no returns occured till the end of the file
    return "";
}

Context

StackExchange Code Review Q#128546, answer score: 3

Revisions (0)

No revisions yet.