0

I have following python code -

def get_subject_from_stream_id_and_subject_id(stream_id, subject_id):
#(stream_id, subject_id): ("subject_name")
return {
    (1, 1): "Accounts",
    (1, 2): "English",
    (1, 3): "Organization of Commerce",
    (2, 1): "Physics",
    (2, 2): "English",
    (2, 3): "Biology"
}.get((stream_id, subject_id), "None")

In this code, I want to get subject name from the integer pair combination i.e. stream_id, subject_id e.g. (1, 2) is for English. It was implemented using python tuple.

I want to implement the same piece of code in Java.

Could someone write this in a better way in Java?

public String getSubjectFromStreamIdAndSubjectId(int streamId, int subjectId) {
  switch (streamId) {
    case 1:
        switch (subjectId) {
        case 1:
            return "Accounts";
        case 2:
            return "English";
        case 3:
            return "Organization of Commerce";
        default:
            return null;
        }

    case 2:
        switch (subjectId) {
        case 1:
            return "Physics";
        case 2:
            return "English";
        case 3:
            return "Biology";
        default:
            return null;
        }
    default:
        return null;
   }
}

Thank you.

A J Brockberg
  • 563
  • 2
  • 5
  • 18

3 Answers3

2

I don't like the solution presented in the duplication suggestion Switching on a pair of `int`s. for two reasons:

  1. The solution relies on external logic (Integer.valueOf() and switch of String) while it is not probable, the implementations may vary in future JDK releases
  2. the switch-case was designed as shorthand for series of if statements. is not the best solution for mapping input to output values. A better solution is to utilize the Map data structure

The proper solution in my eyes would involve some kind Java Tuple. while there is no Tuple in the JDK, one can be easily constructed as user defined class. In fact, there is already an SO answer about that: A Java collection of value pairs? (tuples?) so if we use the class from the above-mentioned answer as Map key, the solution is fairly easy and much more extensible (you could, for instance, load the map from an external resource like text file or DB table):

// initialized using instance initializer
Map<Pair<Integer, Integer>, String> streamIdAndSubjectIdMap = new HashMap<>()
{
    {
        put(new Pair(1, 1), "Accounts");
        put(new Pair(1, 2), "English");
        put(new Pair(1, 3), "Organization of Commerce");
    }
};

public String getSubjectFromStreamIdAndSubjectId(int streamId, int subjectId) {
    return streamIdAndSubjectIdMap.get(new Pair<>(streamId, subjectId));
}
Sharon Ben Asher
  • 13,849
  • 5
  • 33
  • 47
0

Personally, i would really recommend to not use the switch statement here, since any hacks (like String concatenation) will just complicate things. However, you could refactor this method to use a regular if expression with a return statement.

public static String getSubject(int streamId, int subjectId) {
    Pair<Integer> pair = Pair.of(streamId, subjectId);

    if (pair.equals(Pair.of(1, 1))) {
        return "Subject";
    }
    if (pair.equals(Pair.of(1, 2))) {
        return "English";
    }
    if (pair.equals(Pair.of(1, 3))) {
        return "Organization of Commerce";
    }
    if (pair.equals(Pair.of(2, 1))) {
        return "Physics";
    }
    if (pair.equals(Pair.of(2, 2))) {
        return "English";
    }
    if (pair.equals(Pair.of(2, 3))) {
        return "Biology";
    }

    return null;
}

At least to my eye, this looks very clean and there is no need to use an if-else expression. One thing to note here is that the Pair class needs to be implemented correctly regarding equals and hashCode for this to work. An example implementation might be the following (tough it can still be extended):

public class Pair<T> {

    private T first;
    private T second;

    public static <T> Pair<T> of(T first, T second) {
        return new Pair<>(first, second);
    }

    private Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public T getSecond() {
        return second;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pair<?> pair = (Pair<?>) o;
        return Objects.equals(first, pair.first) &&
                Objects.equals(second, pair.second);
    }

    @Override
    public int hashCode() {
        return Objects.hash(first, second);
    }
}
Glains
  • 2,773
  • 3
  • 16
  • 30
0

It is not always recomended but in your case i would go with a nested ternary operator. If you have more combinations than given in your example this aproach could end in confusing, unreadable code. But if you have only those well defined cases:

public static String getSubjectFromStreamIdAndSubjectId(int stream_id, int subject_id) {
    return stream_id == 1 ?
            subject_id == 1 ? "Accounts" : 
            subject_id == 2 ? "English" : 
            subject_id == 3 ? "Organization of Commerce" : "None":
           stream_id == 2 ?
            subject_id == 1 ? "Physics" : 
            subject_id == 2 ? "English" : 
            subject_id == 3 ? "Biology" : "None":
           "None";
}
Eritrean
  • 15,851
  • 3
  • 22
  • 28