-1

I have a set of unformatted strings that I need to come out formatted. I have the full list for both.

Here's a subset of it:

"in room"     => "ROOM"
"in big room" => "BIG ROOM"
"in building" => "BUILDING"
"in street"   => "STREET"
"in house"    => "STANDARD"
"Room box"    => "ROOM"
"Big room box"=> "BIG ROOM"
"Street box"  => "STREET"
"Box"         => "STANDARD"
default value => "STANDARD"

I was told to use an enum in so that I don't have tons of ifs, but I'm not sure how that would help. My enum will look something like this:

public enum BoxLocation {
  STANDARD("STANDARD"),
  ROOM("ROOM"),
  BIG_ROOM("BIG ROOM"),
  ...

But I don't get how that will help avoiding tons of ifs.

How should I bind one or more (never more than 2, except for the default value) unformatted strings to a formatted string, what the cleanest way? I was thinking something like:

if(boxLocation.equals("in room") || boxLocation.equals("Room box"))
    boxLocation = BoxLocation.ROOM;

But then how does it help to have an enum, couldn't I just use this?

boxLocation = "ROOM";

EDIT: Some formatted values have spaces, that changes things with enums. I edited the list.

Teleporting Goat
  • 417
  • 1
  • 6
  • 20
  • If your dictionary is String to String, use a `Map` and a standard `map.get(value)` eliminates any if statements. – Compass Mar 04 '19 at 14:23
  • That's not how you define enum constants. Please [review](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html). – RealSkeptic Mar 04 '19 at 14:26
  • Your enum doesn't help, because you didn't implement it correctly. I guess the plan was having something like `STANDARD("in room")` and then a method like `public BoxLocation getFrom(String s)` which looks for the matching "in room" and returns the corresponding enum entry. – Tom Mar 04 '19 at 14:27
  • 1
    Possible duplicate of [how to get the enum type by its attribute?](https://stackoverflow.com/questions/7888560/how-to-get-the-enum-type-by-its-attribute) – Tom Mar 04 '19 at 14:29
  • Someone linked [this similar question](https://stackoverflow.com/questions/4197988/java-enum-valueof-with-multiple-values/4198066#4198066) and deleted but it's relevant so I'm adding it again. – Teleporting Goat Mar 04 '19 at 14:29
  • Regarding my linked dupe: don't use the accepted answer, but this https://stackoverflow.com/a/7888655/3824919 instead. It shows you the general approach. – Tom Mar 04 '19 at 14:30
  • @RealSkeptic You're right, I edited my enum definition. – Teleporting Goat Mar 04 '19 at 16:56

4 Answers4

2

You could use something like this, assuming the enum is selected if the name of the enum is contained as a word in the sentence:

public enum BoxLocation {
    ROOM, BUILDING, STREET, STANDARD, BIG_ROOM;

    private final Pattern pattern = Pattern.compile(
        name().replace('_', ' '), // take care of underscores
        Pattern.CASE_INSENSITIVE
    );

    public static BoxLocation fromValue(String sentence) {
        for (BoxLocation value : BoxLocation.values()) {
            if (value.pattern.matcher(sentence).find()) {
                return value;
            }
        }
        return STANDARD;
    }
}

Which can then be called like this:

BoxLocation boxLoc = BoxLocation.fromValue("In room");

This approach uses a different Pattern (class used for regular expressions in java) for every enum, its regex equivalent would look like this: /enumname/i where enumname is the name of the enum and i is the case insensitive flag.

Lino
  • 19,604
  • 6
  • 47
  • 65
  • Where do you match unformatted strings to formatted ones ? There must be somewhere you define that `"Box"`and `"in house"` both correspond to `"STANDARD"`, for example. – Teleporting Goat Mar 04 '19 at 14:37
  • @TeleportingGoat The last line in the `BoxLocation.fromValue()` will handle these cases. That line will only be reached if and only if, no previous enum has matched – Lino Mar 04 '19 at 14:39
  • I edited my questions, one of the value has a space in it. How does that work with enums? Can it be `ROOM, "BIG ROOM", BUILDING, STREET, STANDARD;` ? – Teleporting Goat Mar 04 '19 at 15:01
  • @TeleportingGoat see my edit, by replacing the underscore with a space at the creation time of the regex – Lino Mar 04 '19 at 15:03
2

Your enum should look like :

public enum BoxLocation{
    ROOM("ROOM","in room", "Room box"),
    BUILDING("BUILDING","in building"),
    STREET("STREET","in street", "Street box"),
    STANDARD("STANDARD","in house"),
    BIG_ROOM("BIG ROOM", "in big room");

    private final List<String> values;

    BoxLocation(String ...values) {
        this.values = Arrays.asList(values);
    }
    public List<String> getValues() {
        return values;
    }
    public static String find(String name) {
        for (BoxLocation bl : BoxLocation.values()) {
            if (bl.getValues().contains(name)) {
                return bl.values.get(0);
            }
        }
        return BoxLocation.STANDARD.values.get(0);
    }
}

and retrieve the corresponding value to any string using the find method

public static void main(String[] args) {
    String boxLoc = BoxLocation.find("xyz");
    System.out.println(boxLoc);
}
Eritrean
  • 15,851
  • 3
  • 22
  • 28
  • Small improvement could be, by using a `Set` instead of a `List`: `new HashSet<>(Arrays.asList(values))` – Lino Mar 04 '19 at 14:55
  • 1
    This is a very inefficient way to look up each string as compared to a proper hash map. – RealSkeptic Mar 04 '19 at 14:55
  • That's almost what I need! But (I should have mentioned it) one of my values has a space, so I can't return the identifier. I think I need to add the formatted value as a first value and return that. Ex: `BIG_ROOM("BIG ROOM", "in big room")` How can I return the first if the value is in one of the possible ones? – Teleporting Goat Mar 04 '19 at 14:58
  • @TeleportingGoat Not sure if this was what you wanted. I have edited my answer (Changed the return type to string) to return the first string from the enum parameters. – Eritrean Mar 04 '19 at 15:35
  • @RealSkeptic With a hashmap, there's really no point in using an enum at all, is there? (I mean if I have a good reason not to use an enum it's good too.) – Teleporting Goat Mar 04 '19 at 17:05
  • @TeleportingGoat indeed, you don't need an enum, but consider whether you use that "canonical" string later for anything else, particularly comparisons or decision making. If you do, it's always best to use an enum constant than a `String`. Also, using an enum ensures you do not make mistakes when you fill the map (e.g. mapping to `BIG ROM` instead of `BIG ROOM`). – RealSkeptic Mar 04 '19 at 17:23
  • @RealSkeptic Yeah I thought using an enum was a good idea because several classes need to make the conversion between non-canonical to canonical. – Teleporting Goat Mar 04 '19 at 18:24
1

You need to introduce some sort of property field to your Enum, on top of the key; something like this:

public enum BoxLocation {

    ROOM("in room"), 
    BUILDING("in building"), 
   ...

    private String definition;

    BoxLocation(String definition) {
        this.definition = definition;
    }
}

Then, in your code, you can do like

boxLocation.getDefinition().equals("in room")
Or (BoxLocation.from("in room"))

See also Tom's message above and http://tutorials.jenkov.com/java/enums.html#enum-valueof for more pointers

Nestor Milyaev
  • 5,845
  • 2
  • 35
  • 51
0

What you can try to do with enums is comparing String content to the String representation of enum values, like this:

public class Solution {

    public static void main(String[] args) {
        String boxLocation = "in room";

        if (boxLocation.toUpperCase().contains(Locations.ROOM.toString())) {
            System.out.println("The box is in the room");
        } else {
            System.err.println("The box is not in the room");
        }
    }

    enum Locations {
        ROOM,
        BUILDING,
        STREET
    }
}
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • I'm not looking for full sentences. I really need the uppercase values I have in my example. I'm doing a database insert with values taken from a badly formatted csv. – Teleporting Goat Mar 04 '19 at 14:31
  • Plus, `contains` is not an option. I edited my question to show it better (`"in house"` => `"STANDARD"`). – Teleporting Goat Mar 04 '19 at 14:37