5
\$\begingroup\$

Inspired by Reddit r/dailyprogrammer

Help! My keyboard is broken, only a few keys work any more. If I tell you what keys work, can you tell me what words I can write?

The program works fine, not really any additions I wish to make to it. Was just wanting some general feedback on the layout, structure and efficiency.

Note: The dictionary used can be found in the given link.

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * Created on 7/1/2016.
 *
 * Inspired by r/dailyprogrammer
 * https://www.reddit.com/r/dailyprogrammer/comments/3pcb3i/20151019_challenge_237_easy_broken_keyboard/
 */


public class BrokenKeyboard {

    private static List<String> wordDict;
    private static final String dictPath = "Dictionaries/EnglishWords.txt";

    /**
     * The main acquires input from the user then
     * applies it to the other below methods.
     * @param args Unused.
     */

    public static void main(String args[]) {
        BrokenKeyboard.setDict(dictPath); // Stores dictionary in list.
        System.out.println("Processing...");
        System.out.println(String.join(", ", findMatches("bikn", wordDict)));
    }

    /**
     * Used to load a given text file into memory.
     * @param filePath Used to specify the path of the file that needs to be loaded into memory.
     */
    private static void setDict(String filePath){
        try {
            wordDict = Files.readAllLines(new File(filePath).toPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Used to compare a given key to the text file loaded into memory.
     * @param key The inputted set of characters compared to the given dictionary.
     * @param dict The list of words that will be used to compare to the key.
     * @return The list of matches made when comparing the key with the given dictionary.
     */
    private static List<String> findMatches(String key, List<String> dict){
        List<String> matchList = new ArrayList<>();
        for(String word : dict){
            String temp = word; // Stores word so that it can be manipulated.
            for(char character : key.toCharArray()) {
                temp = temp.replace(Character.toString(character), "");
            }
            if(temp.equals("")){
                matchList.add(word);
            }
        }
        return matchList;
    }
}
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

setDict() is a bit of a misnomer; loadDict() would be more accurate. Since the loaded dictionary becomes part of the global state, it would be more appropriate to make it an instance variable than a static variable. If loading fails, you shouldn't just print a stack trace and attempt to continue execution — that defeats the purpose of exceptions. In general, if you don't have a good way to handle an exception, just propagate it (by declaring throws IOException).

Your technique for checking whether a word consists solely of a limited set of characters is to do repeated string replacement. That would cause a lot of temporary objects to be allocated and discarded. A more appropriate tool to use would be a regular expression match (using Pattern.compile("^[bikn]*$"), for example).

Since this is a filtering exercise, it would be more eloquently written using Stream<String>.filter(Predicate) instead of List<String>.

Although String args[] is syntactically valid, String[] args is considered more idiomatic in Java, since String[] is considered the type.

Suggested solution

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BrokenKeyboard {
    public static Predicate<String> typableUsing(String availableKeys) {
        // CAVEAT: availableKeys is assumed not to contain regex metacharacters
        // such as ^ [ ] - \
        return Pattern.compile("^[" + availableKeys + "]*$").asPredicate();
    }

    public static Stream<String> dictionary(File f) throws IOException {
        return Files.lines(f.toPath());
    }

    public static void main(String[] args) throws IOException {
        System.out.println(
            BrokenKeyboard.dictionary(new File(args[1]))
                          .filter(BrokenKeyboard.typableUsing(args[0]))
                          .collect(Collectors.joining(", "))
        );
    }
}

It is possible to resolve the caveat using Pattern.quote(). I'll leave it as an exercise to the reader. =)

\$\endgroup\$
1
  • \$\begingroup\$ Hey success, I have very little experience with Java's Pattern class. I did some research and tried applying said research to the code but its producing unwanted results. If you could jot down a few lines on how to use the compiled pattern that would be awesome. \$\endgroup\$
    – JSextonn
    Commented Jul 5, 2016 at 5:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.