im creating a method where a user input has to type A/B to get a return. how do i make my code case insensitive to make it work?
String examcode (String code) {
if (code.compareToIgnoreCase("A")) {
EXAM_NAME = "Subject A";
}
else if (code.compareToIgnoreCase("B")) {
EXAM_NAME = "Subject B";
}
else {
EXAM_NAME = "no code";
}
return EXAM_NAME;
}
Use equalsIgnoreCase(), not compareToIgnoreCase().
if (code.equalsIgnoreCase("A")) {
EXAM_NAME = "Subject A";
} //...
If you want to use compareToIgnoreCase() to check equality, you need to compare the result to 0:
if (code.compareToIgnoreCase("A") == 0) {
//...
just like any other compareTo method. But there's no advantage to using it here.
Edit:
An alternative to your entire approach might be to use a case-insensitive map instead. Something like this:
Map<String, String> subjects = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
subjects.put("A", "Subject A");
subjects.put("B", "Subject B");
//...
String subjectName = subjects.get(code);
if ( subjectName == null ) {
subjectName = "no code";
}
Or, you could also use a normal case-sensitive map and just convert all keys to lower case before doing map operations.
Just use the method -
String.equalsIgnoreCase(java.lang.String) which returns a boolean and does the job for what you are looking for.
if (code.compareToIgnoreCase("A")) is not valid since compareToIgnoreCase returns int.
Reading the javadoc for String would have provided your answer for you. Google would have turned that right up.
Use code.equalsIgnoreCase("your string") which returns a boolean value, OR
use code.compareToIgnoreCase("your string") == 0. compareToIgnoreCase gives you a way to determine the "order" of two strings. Again, enjoy the javadoc.
Use same code but equalsIgnoreCase(String) instead of compareToIgnoreCase(String).
What you want is String#equalsIgnoreCase(String) which returns a boolean.
use String.toLowerCase() (or String.toUpperCase()) before comparing.
if (code.toLowerCase().equals("a")) {
EXAM_NAME = "Subject A";
}
Note that like all string methods, code.toLowerCase does not change the string stored in 'code', but returns a copy of 'code's value all in lower case.
Related
I have a java class with 3 boolean property like this
boolean isActive;
boolean isEnable;
boolean isNew;
every property is related to an enum (e.g. ACTIVE,ENABLE,NEW).
I want to have 2 lists of enum. One which has only the enums related to true property value and one for the false one.
just to be clear. using if-else statement I could have
Set<FlagEnum> flagSet = new HashSet<>();
Set<FlagEnum> falseFlagSet = new HashSet<>();
if (object.isActive()) {
flagSet.add(ACTIVE);
} else {
falseFlagSet.add(ACTIVE);
}
if (object.isEnable()) {
flagSet.add(ENABLE);
} else {
falseFlagSet.add(ENABLE);
}
if (object.isNew()) {
flagSet.add(NEW);
} else {
falseFlagSet.add(NEW);
}
is there a way to avoid all these if-else?
I tried with something like
Map<boolean, List<Pair<boolean, FlagEnum>>> res = Stream.of(
new Pair<>(object.isActive(), ACTIVE),
new Pair<>(object.isNew(), NEW),
new Pair<>(object.isEnable(), ENABLE))
.collect(Collectors.partitioningBy(Pair::getKey));
but the resulted structure is an additional complexity which I would like to avoid.
In my real case, I have more than 15 boolean properties...
You can simplify this in various ways. Which of them make sense, depends on your exact requirements.
You can derive the falseFlagSet trivially from the flagSet using EnumSet.complementOf after populating the flagSet:
EnumSet<FlagEnum> falseFlagSet = EnumSet.complementOf(flagSet);
This assumes that all FlagEnum values have corresponding flags. If that's not the case then you need to construct a EnumSet with all enums that have flags and subtract flagSet from that using removeAll.
#1 already removes the need for the else in your cascade, simplifying the code to
if (object.isActive()) {
flagSet.add(ACTIVE);
}
if (object.isEnable()) {
flagSet.add(ENABLE);
}
if (object.isNew()) {
flagSet.add(NEW);
}
If you have enough different flags, then you can create a mapping from getter method to FlagEnum value like this:
Map<Function<YourClass,Boolean>,FlagEnum> GETTERS = Map.of(
YourClass::isActive, FlagEnum.ACTIVE,
YourClass::isNew, FlagEnum.NEW,
YourClass::isEnable, FlagEnum.ENABLE);
Then you can use this to make the whole process data-driven:
EnumSet<FlagEnum> getFlagSet(YourClass yourObject) {
EnumSet<FlagEnum> result = EnumSet.noneOf(FlagEnum.class);
for (Map.Entry<Function<YourClass,Boolean>, FlagEnum> getter : GETTERS.entrySet()) {
if (getter.getKey().apply(yourObject)) {
result.add(getter.getValue());
}
}
return result;
}
If the number of flags is very big, then you could switch entirely to reflection and detect the flags and matching getters dynamically using string comparison, but I would not suggest that approach. If you need something like that then you probably should switch to a framework that supports that kind of feature and not implement it yourself.
That last two obviously only makes sense when the number of flags is big. If it's actually just 3 flags, then I wouldn't mind and just have 3 simple if statements.
As a slight tangent: GETTERS above should definitely be an immutable map (wrap it in Collections.unmodifiableMap or use something like Guava ImmutableMap) and it could be argued that the same applies to the return value of the getFlagSet method. I've left those out for succinctness.
You can use a private helper method for this.
private void addFlagSet(boolean condition, FlagEnum flagEnum,
Set<FlagEnum> flagSet, Set<FlagEnum> falseFlagSet) {
Set<FlagEnum> chosenFlagSet = condition ? flagSet: falseFlagSet;
chosenFlagSet.add(flagEnum);
}
Call it as:
addFlagSet(object.isActive(), FlagEnum.ACIVE, flagSet, falseFlagSet);
addFlagSet(object.isNew(), FlagEnum.NEW, flagSet, falseFlagSet);
addFlagSet(object.isEnable(), FlagEnum.ENABLE, flagSet, falseFlagSet);
You could probably use Reflection to get all methods, then check if a getReturnType() == boolean.class. Problem is the connection between the method's name and the enum. If every single one is named like the method without the 'is', you could use FlagEnum.valueOf() to retrieve the enum value from the method name and use it.
I think this could be the easiest and clearest way to do what I need
Map<Boolean, Set<FlagEnum>> flagMap = new HashMap<>();
flagMap.computeIfAbsent(object.isActive(), h -> new HashSet()).add(ACTIVE);
flagMap.computeIfAbsent(object.isEnabled(), h -> new HashSet()).add(ENABLE);
flagMap.computeIfAbsent(object.isNew(), h -> new HashSet()).add(NEW);
//to get TRUE set simply :
flagMap.get(true);
what do you think?
I'm iterating a set object to find a particular value. Is there any short way to fetch instead of iterating it? Here is my code
for(Tree t : assignedTrees) {
println t.treeName;
}
The above code will return the expected value.
assignedTrees is the set object
Set<Tree> assignedTrees = new HashSet<Tree>()
println assignedTrees will return
[Tree{id=null, treeName=Mango}]
Can I fetch the treeName instead of iterating?
You can fetch an object from a set by calling mySet.get(object). However, in your case you wish to fetch an object based on one of its attributes. The best way to do this is with a map - e.g.
Map<String, Tree> trees = new HashMap<>();
trees.put(treeObject.treeName, treeObject);
Tree myTree = trees.get("myTreeName");
Note that if you're putting your own objects into sets or maps, you must override the equals and hashcode methods, or very strange things will happen.
In general you can use lambda to find any/first element that fullfils any condition. For example:
Set<Integer> coolStrings = new HashSet<String>();
coolStrings.add("HEHE")
coolStrings.add("NOPE")
coolStrings.add("JP2GMD")
coolStrings.add("1234")
try{
String nice =
coolStrings.stream().filter(
(str) -> { return str.equals("JP2GMD") ||
str.equals("2137"); }
}).findFirst().get();
)
System.out.println("Yay, i found a REALLY cool string! : " + nice);
}
catch(NoSuchElementException e){
System.out.println("Not even one awesome string was found :(");
}
It will print "JP2GMD"
(I didn't compile it, there might be some minor syntax errors)
Working with Stream class is extremally handy (as for java standards)
I have a method which maps a given string to another string like if the input to the method is "RS256" it will return "SHA256WithRSA" and many more. My method is given below
public String getAlgorithm(String alg) {
// The internal crypto provider uses different alg names
switch(alg) {
case "RSA256" : return "SHA256withRSA";
case "SHA384" : return "SHA384withRSA";
case "SHA512" : return "SHA512withRSA";
}
throw new Exception("Not supported");
}
is there any other way to do this (i don't want to use MAP). I'm looking to see if there is any design pattern or any OOP concept to do this.
Use real map, I mean java.util.Map which keeps key-value pair ex. Map<Key,Value>
Map<String,String> map= new HashMap<String,String>();
map.add("RSA256","SHA256withRSA");
map.add("SHA384","SHA384withRSA");
map.add("SHA512","SHA512withRSA");
...
public String getAlgorithm(String alg) {
return map.get(alg);
}
You are actually written a Facade Pattern here, I think you are wrapping some kind of library. switch-case statement should be fine.
Using map introduces overhead, so better no to use it.
You can use if-else to check alg equals to your condition and return value similar to this. But current way is very similar to this.
Why you can't use Map? that is the better way.
Map<String,String> algoMap=new HashMap<>(String,String);
Now you can put algoMap.put("algoName","Value")
Use HashMap
HashMap<String, String> newMap = new HashMap<String, String>();
newMap.put("RSA256", "SHA256withRSA");
newMap.put("SHA384", "SHA384withRSA");
newMap.put("SHA512", "SHA512withRSA");
String value = (String) newMap.get("RS256");
You can use enum types as well, but either way you have to use a switch statement without maps.
enum Algorithm {
RSA256,
SHA384,
SHA512;
public String name(String pValue) throws Exception {
switch(this) {
case RSA256:
return "SHA256withRSA";
case SHA384:
return "SHA384withRSA";
case SHA512:
return "SHA512withRSA";
default:
throw new Exception("Not supported");
}
}
}
In my app, I need to branch out if the input matches some specific 20 entries.
I thought of using an enum
public enum dateRule { is_on, is_not_on, is_before,...}
and a switch on the enum constant to do a function
switch(dateRule.valueOf(input))
{
case is_on :
case is_not_on :
case is_before :
.
.
.
// function()
break;
}
But the input strings will be like 'is on', 'is not on', 'is before' etc without _ between words.
I learnt that an enum cannot have constants containing space.
Possible ways I could make out:
1, Using if statement to compare 20 possible inputs that giving a long if statement like
if(input.equals("is on") ||
input.equals("is not on") ||
input.equals("is before") ...) { // function() }
2, Work on the input to insert _ between words but even other input strings that don't come under this 20 can have multiple words.
Is there a better way to implement this?
You can define your own version of valueOf method inside the enum (just don't call it valueOf).
public enum State {
IS_ON,
IS_OFF;
public static State translate(String value) {
return valueOf(value.toUpperCase().replace(' ', '_'));
}
}
Simply use it like before.
State state = State.translate("is on");
The earlier switch statement would still work.
It is possible to seperate the enum identifier from the value. Something like this:
public enum MyEnumType
{
IS_BEFORE("is before"),
IS_ON("is on"),
IS_NOT_ON("is not on")
public final String value;
MyEnumType(final String value)
{
this.value = value;
}
}
You can also add methods to the enum-type (the method can have arguments as well), something like this:
public boolean isOnOrNotOn()
{
return (this.value.contentEquals(IS_ON) || this.value.contentEquals(IS_NOT_ON));
}
Use in switch:
switch(dateRule.valueOf(input))
{
case IS_ON: ...
case IS_NOT_ON: ...
case IS_BEFORE: ...
}
And when you get the value of IS_ON like for example System.out.println(IS_ON) it will show is on.
If you're using Java 7, you can also choose the middle road here, and do a switch statement with Strings:
switch (input) {
case "is on":
// do stuff
break;
case "is not on":
// etc
}
You're not really breaking the concept up enough, both solutions are brittle...
Look at your syntax
"is", can remove, seems to be ubiquitous
"not", optional, apply a ! to the output comparison
on, before, after, apply comparisons.
So do a split between spaces. Parse the split words to ensure they exist in the syntax definition and then do a step-by-step evaluation of the expression passed in. This will allow you to easily extend the syntax (without having to add an "is" and "is not" for each combination and keep your code easy to read.
Having multiple conditions munged into one for the purposes of switch statements leads to huge bloat over time.
Thanks for the suggestions. They guided me here.
This is almost same as other answers, just a bit simplified.
To summarize, I need to compare the input string with a set of 20 strings and
if they match, do something. Else, do something else.
Static set of strings to which input needs to be compared :
is on,is not on,is before,is after, etc 20 entries
I created an enum
public enum dateRules
{
is_on
,is_not_on
,is_before
,is_after
.
.
.
}
and switching on formatted value of input
if(isARule(in = input.replace(" ","_"))
{
switch(dateRule.valueOf(in))
{
case is_on,
case is_not_on,
case is_before, ...
}
}
I copied the formatted value of 'input' to 'in' so that I can reuse input without another replace of '_' with ' '.
private static boolean isARule(String value)
{
for(dateRule rule : dateRule.values())
{
if(rule.toString().equals(value))
{
return true;
}
}
return false;
}
Problem solved.
Reference : https://stackoverflow.com/a/4936895/1297564
I have the following if statement:
String newStr4 = strr.split("2012")[0];
if (newStr4.startsWith("Mon")) {
str4.add(newStr4);
}
I want it to include startsWith Mon Tues Weds Thurs Friday etc. Is there a simple way to this when using strings? I tried || but it didn't work.
Do you mean this:
if (newStr4.startsWith("Mon") || newStr4.startsWith("Tues") || ...)
Or you could use regular expression:
if (newStr4.matches("(Mon|Tues|Wed|Thurs|Fri).*"))
Besides the solutions presented already, you could use the Apache Commons Lang library:
if(StringUtils.startsWithAny(newStr4, new String[] {"Mon","Tues",...})) {
//whatever
}
Update: the introduction of varargs at some point makes the call simpler now:
StringUtils.startsWithAny(newStr4, "Mon", "Tues",...)
No one mentioned Stream so far, so here it is:
if (Stream.of("Mon", "Tues", "Wed", "Thurs", "Fri").anyMatch(s -> newStr4.startsWith(s)))
A simple solution is:
if (newStr4.startsWith("Mon") || newStr4.startsWith("Tue") || newStr4.startsWith("Wed"))
// ... you get the idea ...
A fancier solution would be:
List<String> days = Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT");
String day = newStr4.substring(0, 3).toUpperCase();
if (days.contains(day)) {
// ...
}
it is even simpler and more neat this way:
let newStr4 = strr.split("2012")[0];
if (['Mon', 'Tues', 'Weds', 'Thurs', 'Friday'].some(word => newStr4.startsWith(word))) {
str4.add(newStr4);
}
Of course, be mindful that your program will only be useful in english speaking countries if you detect dates this way. You might want to consider:
Set<String> dayNames = Calendar.getInstance()
.getDisplayNames(Calendar.DAY_OF_WEEK,
Calendar.SHORT,
Locale.getDefault())
.keySet();
From there you can use .startsWith or .matches or whatever other method that others have mentioned above. This way you get the default locale for the jvm. You could always pass in the locale (and maybe default it to the system locale if it's null) as well to be more robust.
Call stream() on the list itself if you already have a List of elements:
List<String> myItems = Arrays.asList("a", "b", "c");
Like this:
boolean result = myItems.stream().anyMatch(s -> newArg4.startsWith(s));
Instead of using Stream.of...
if(newStr4.startsWith("Mon") || newStr4.startsWith("Tues") || newStr4.startsWith("Weds") .. etc)
You need to include the whole str.startsWith(otherStr) for each item, since || only works with boolean expressions (true or false).
There are other options if you have a lot of things to check, like regular expressions, but they tend to be slower and more complicated regular expressions are generally harder to read.
An example regular expression for detecting day name abbreviations would be:
if(Pattern.matches("Mon|Tues|Wed|Thurs|Fri", stringToCheck)) {
When you say you tried to use OR, how exactly did you try and use it? In your case, what you will need to do would be something like so:
String newStr4 = strr.split("2012")[0];
if(newStr4.startsWith("Mon") || newStr4.startsWith("Tues")...)
str4.add(newStr4);
If the task is to check if string matches any of the multiple values ignoring case, there are 2 options:
Based on the answer by dejvuth, you can do a batch-checking ignoring case as follows:
if (Stream.of("Mon", "Tues", "Wed", "Thurs", "Fri")
.anyMatch(s -> StringUtils.startsWithIgnoreCase(newStr4, s)));
Use the following method based on an implementation in StringUtils:
public static boolean startsWithAnyIgnoreCase(
CharSequence sequence, CharSequence... searchStrings) {
if (!isEmpty(sequence) && !ArrayUtils.isEmpty(searchStrings)) {
CharSequence[] searchStringsArray = searchStrings;
int length = searchStrings.length;
for (int i = 0; i < length; ++i) {
CharSequence searchString = searchStringsArray[i];
if (startsWithIgnoreCase(sequence, searchString)) {
return true;
}
}
}
return false;
}