String array to multiple objects - java

I have a string array
String[] weekDays
weekDays represents days of week. for eg
{1, 2, 4} means Monday, Tuesday, Thusday
{2,5,6,7} means Tuesday, Friday, Saturday, Sunday
I have seven boolean representing 7 days of week and have to set true or false on the basis of weekDays.
My code
private static void setWeekDays(final Object object, final String[] weekDays) {
for (String day : weekDays) {
if( day.equalsIgnoreCase("1")) {
object.setMonday(true);
} else if( day.equalsIgnoreCase("2")) {
object.setTuesday(true);
} else if( day.equalsIgnoreCase("3")) {
object.setWednesday(true);
} else if( day.equalsIgnoreCase("4")) {
object.setThrusday(true);
} else if( day.equalsIgnoreCase("5")) {
object.setFriday(true);
} else if( day.equalsIgnoreCase("6")) {
object.setSaturday(true);
} else if( day.equalsIgnoreCase("7")) {
object.setSunday(true);
}
}
}
But it has so many if else statements and before performing this i explicitly set all booleans to be false.
Is there any other simple way to do the same thing? Any suggestion ??

I think you can try to use ENUM for this.
For example
public enum WeekDay {
MONDAY("1");
private String value;
private WeekDay(String value) {
this.value = value;
}
public static WeekDay find(String value) {
for (WeekDay weekDay : values()) {
if (weekDay.value.equals(value)) {
return weekDay;
}
}
}
So then you can use this ENUM as field of your DTO.

Define an Enum to be your days of the week:
enum DayOfWeek {
MONDAY, TUESDAY, etc.
}
Create a map to go from string to day:
Map<String, DayOfWeek> dayByString = new HashMap<String, DayOfWeek>();
Fill the map with all lookups you want, i.e.
dayByString.put("1", DayOfWeek.MONDAY);
dayByString.put("2", DayOfWeek.TUESDAY);
Now to look up the day just do:
DayOfWeek day = dayByString.get(str);
This will return null if there is no match - or the matching day of the week.
Now instead of your 7 booleans use an EnumSet:
Set<DayOfWeek> days = new EnumSet<DayOfWeek>(DayOfWeek.class);
Internally that will use a bitfield to represent the days so will be incredibly fast and space efficient.
To set the flag do days.add(day);
To unset it do days.remove(day);
To check if it is set do days.contains(day); or days.contains(DayOfWeek.MONDAY);.
private static void setWeekDays(EnumSet<DayOfWeek> set, final String[] weekDays) {
set.clear();
for (String day : weekDays) {
set.add(dayByString.get(day));
}
}
You are done, that's all you need.
EnumSet is the correct way to store this. There are other options but 7 booleans is flat out wrong.

You can try following:
Add following code in your DTO Object
List weekDaysList;
private void setWeekDays(String[] weekDays){
weekDaysList = Arrays.asList(weekDays);
}
private boolean isWeekdaySet(String weekday){
if (weekDaysList == null || weekDaysList.size() == 0){
return false;
}
return weekDaysList.contains(weekday);
}
update each of the getMonday, getTuesday method as follows:
public boolean getMonday() {
return isWeekdaySet("1");
}
public boolean getTuesday(){
return isWeekdaySet("2");
}
I hope this helps.

If the DTO class can NOT be modified, you could try to make a method mapping previously, and use reflection to call the target method, like this:
private static Method[] methods;
private static void init() throws Exception {
Class klass = BusTravelDetailDTO.class;
String[] methodNames = new String[]{null, "setMonday", "setTuesday", "setSunday"};
methods = new Method[methodNames.length];
for (int i = 0; i < methods.length; i++) {
if(methodNames[i] != null) {
methods[i] = klass.getMethod(methodNames[i], Boolean.class);
}
}
}
private static void setWeekDays(final Object object, final String[] weekDays) {
for (String day : weekDays) {
methods[Integer.parseInt(day)].invoke(object, Boolean.TRUE);
}
}
But, since you have only seven choices, if-else might be the most simple and efficient way.
If the DTO class can be modifie, use enum instead of seven boolean flags.

Related

Take a value from a list through a getter method?

I have 3 classes, Human, Date, and Zodiac. In Date I have two int types, month and day. I have normal constructor and getter. In Human I have a String name and a birthday from the type Date.
My Class Date:
public class Date {
private int month;
private int day;
public Date(int month, int day) {
this.month = month;
this.day = day;
}
public int getMonth() { return month;}
public int getDay() {return day;}
My Class Human
public class Human {
private String name;
private Date birthday;
public Human(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() { return name;}
public BirthDate getBirthday() { return birthday;}
In My class Zodiac I have a Main where I created some objects. Then I have a method zodiacToHuman were I give a Human his star sign. But this method didn't work at all. The method has a List as Parameter and returns a Map.
My method in class Zodiac:
public static Map<Human, String> zodiacToHuman(List<Human> humanlist){
Map<Human, String> personSign = new HashMap<>();
Human human;
String sign = "";
int day = Date.getDay();
int month = Date.getMonth();
if (month == 1) {
if (day < 20)
sign = "capricornus";
humanSign.put(human, sign);
else
sign = "aquarius";
humanSign.put(human, sign);
}//and so on
}
This is the error I get:
Non-static method 'getDay()' cannot be referenced from a static context
Non-static method 'getMonth()' cannot be referenced from a static context
Variable Human might not have been initialized
Can someone help me?
You can't do
int day = Date.getDay()
Create an object first of the Date class and use it to get the day and month
Data date = new Date()
int day = date.getDay()
int month = date.getMonth()
Also you haven't initialised your Human class object.
You can write
Human human = new Human(some_day, some_month)
Let's change the name of Date to more precise MonthWithDay.
We can shorten the code of that class by making it a record. By default, the implicitly created getter methods are named the same as the member field name.
public record MonthWithDay( int month , int day ) { }
Similarly we can define your Human class as a record in one short line.
public record Human( String name , MonthWithDay monthDayWhenBorn ) { }
Regarding your method to determine zodiac:
public static Map<Human, String> zodiacToHuman(List<Human> humanlist){ …
… there is no need for static. In your scenario, that seems like a reasonable feature on your Human class.
Tip: In object-oriented programming, using static is not object-oriented. Try to minimize use of static. Use as a last resort only.
public record Human( String name , MonthWithDay monthDayWhenBorn )
{
public String zodiac ( )
{
int day = this.monthDayWhenBorn.day();
int month = this.monthDayWhenBorn.month();
if ( month == 1 )
{
if ( day < 20 )
{ return "capricornus"; }
else
{ return "aquarius"; }
}
return "other";
}
}
Populate some example data.
List < Human > humans =
List.of(
new Human( "Alice" , new MonthWithDay( 1 , 11 ) ) ,
new Human( "Alice" , new MonthWithDay( 1 , 22 ) ) ,
new Human( "Carol" , new MonthWithDay( 11 , 27 ) )
);
Create your map of human to zodiac.
Map< Human , String > mapOfHumanToZodiac = new HashMap<>() ;
Loop through each Human object, interrogate for its zodiac, and place into our map.
for ( Human human : humans )
{
mapOfHumanToZodiac.put( human , human.zodiac() );
}
Dump to console.
System.out.println( "mapOfHumanToZodiac = " + mapOfHumanToZodiac );
mapOfHumanToZodiac = {Human[name=Alice, monthDayWhenBorn=MonthWithDay[month=1, day=11]]=capricornus, Human[name=Alice, monthDayWhenBorn=MonthWithDay[month=1, day=22]]=aquarius, Human[name=Carol, monthDayWhenBorn=MonthWithDay[month=11, day=27]]=other}
By the way, in real work we would define an enum to represent each of the zodiac signs rather than use mere strings. Doing so provides type-safety, ensures valid values (avoids errors from typos in the strings), and makes the code more self-documenting.
java.time
Java comes with an industry-leading framework of date-time classes, found in the java.time package. These classes include a MonthDay class. So no need to invent your own. We can delete your MonthWithDay class.
Tweak the Human class.
public record Human( String name , MonthDay monthDayWhenBorn ) // <-- Use java.time.MonthDay class.
{
public String zodiac ( )
{
int day = this.monthDayWhenBorn.getDayOfMonth(); // <-- Use java.time.MonthDay class.
int month = this.monthDayWhenBorn.getMonthValue(); // <-- Use java.time.MonthDay class.
if ( month == 1 )
{
if ( day < 20 )
{ return "capricornus"; }
else
{ return "aquarius"; }
}
return "other";
}
}
Change how we create the sample data.
List < Human > humans =
List.of(
new Human( "Alice" , MonthDay.of( 1 , 11 ) ) , // <-- Use java.time.MonthDay class.
new Human( "Alice" , MonthDay.of( 1 , 22 ) ) ,
new Human( "Carol" , MonthDay.of( 11 , 27 ) )
);
And we get the same results.
Errors and reasons
Variable Human might not have been initialized
Its not a error its a warning that saying human variable might be null as you have only decalre the variable human. To initialize either you need to create an instance or assign null to it
Human human = new Human(YOUR VALUES);
//or
Human human = null;
Non-static method 'getDay()' cannot be referenced from a static context
Non-static method 'getMonth()' cannot be referenced from a static context
You cannot access public methods of a class directly without creating an object.
NOTE
As per my understanding you are giving each human a sign value.You can achive the same while you are creating each human object and later on create a map from it.
Eg:
public class Human {
private String name;
private Date birthday;
private String sign;
public Human(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
assignZodiac();
}
private void assignZodiac(){
String sign = "";
//getting birhday month and day values
int day = birthday.getDay();
int month = birthday.getMonth();
// your logic of assignment
if (month == 1) {
if (day < 20)
sign = "capricornus";
else
sign = "aquarius";
}//and so on
}
//getter setter
}
Now you can create a map from the list. eg:
// human list has been already created
Map<Human,String> humanSign=newHasmap<>();
for(Human human : humanList) {
humanSign.put(human,human.getSign()) //asuming getSign is the getter for sign filed in Human.
}
Also I would suggest you to change Date class name to something else since java already has a class of the same name. It's just for naming convection
As I understand the humanList contains entries of Human Objects.
You should try iterating over the list, like so
public static Map<Human, String> zodiacToHuman(List<Human> humanlist) {
Map<Human, String> personSign = new HashMap<>();
for (Human human : humanList) {
String sign = "";
int day = human.getBirthday().getDay();
int month = human.getBirthday().getMonth();
if (month == 1) {
if (day < 20) {
sign = "capricornus";
} else {
sign = "aquarius";
}
} //and so on
humanSign.put(human, sign);
}
}

how to fetch next 4 weekdays name from a particular day?

i need to get next few days name from a particular day. like given day name is Friday and i need to get next 4 like Saturday, Sunday, Monday and Tuesday.
Try this .....
String[] days=new String[] {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
private ArrayList<String> getNextFourDays(String day)
{
int index=-1;
int count=0;
for(int i=0;i<days.length;i++) {
if(days[i].equalsIgnoreCase(day)) {
index=i;
break;
}
}
if(index==-1)
{
return null;
}
ArrayList<String> nextFourDays=new ArrayList<>();
for(int j=index+1;count<4;j++) {
if(j>=days.length) {
j=0;
}
nextFourDays.add(days[j]);
count++;
}
return nextFourDays;
}
Note: it will return null if the invalid day is passed other than the string[] days valid and case has no effect
In this case, I would use the java.time DayOfWeek enum for type safety instead of strings suggested by other answers.
If you are using kotlin you can even define an extension function like this:
fun DayOfWeek.getFollowingDays(n: Int): List<DayOfWeek> {
val list: MutableList<DayOfWeek> = mutableListOf()
var tempDay = this
for (i in 0 until n) {
tempDay = tempDay.plus(1)
list.add(tempDay)
}
return list
}
Then you can call the extension function like this:
DayOfWeek.FRIDAY.getFollowingDays(4)
and the result would be
[SATURDAY, SUNDAY, MONDAY, TUESDAY]
The advantage of using enum instead of a string is that the compiler will catch any errors for you and your app will not crash/have undesired behaviour at runtime. If day is represented as a string you can pass any possible string (like "Invalid Day!") which might cause the program to act unexpectedly at runtime. However, by using enums there is no possible way to misuse it, you need to call it on a day defined by the enum otherwise the app won't build.
It's aready answered but you can do it with much cleaner code and moderner APIs: OneLiner
import java.time.DayOfWeek;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Test {
public static void main(String[] args) {
List<DayOfWeek> nextDaysList = IntStream.range(1, 5).mapToObj(x -> DayOfWeek.FRIDAY.plus(x))
.collect(Collectors.toList());
}
}

Compare String in ENUM

I want to implement storing of enabled or disabled features into database row. When some String value is received from them the network I would like to compare it into ENUM.
ENUM:
public enum TerminalConfigurationFeatureBitString {
Authorize("authorize", 0), // index 0 in bit string
Authorize3d("authorize3d", 1), // index 1 in bit String
Sale("sale", 2), // index 2 in bit String
Sale3d("sale3d", 3), // index 3 in bit String
}
Map<TerminalConfigurationFeatureBitString, Boolean> featureMaps =
config.initFromDatabaseValue(optsFromDatabase);
featureMaps.get(transaction.transactionType);
The best way is to use featureMaps.get(TerminalConfigurationFeatureBitString.Sale);
But I don't know the incoming string what would be.
Now I get warning Unlikely argument type String for get(Object) on a Map<TerminalConfigurationFeatureBitString,Boolean>
Is there any other way to make a query into the ENUM without knowing the key?
In cases like these, I often find myself adding a static method getByX which does a lookup based upon a property of the enum:
public enum BitString {
//...
public static Optional<BitString> getByTransactionType(String transactionType)
{
return Arrays.stream(values())
.filter(x -> x.transactionType.equals(transactionType))
.findFirst();
}
}
Usage:
enum TransactionStatus
{
ENABLED, NOT_ENABLED, NOT_SUPPORTED
}
TransactionStatus status = BitString.getBygetByTransactionType(transaction.transactionType)
.map(bitString -> featureMaps.get(bitString))
.map(enabled -> enabled ? TransactionStatus.ENABLED : TransactionStatus.NOT_ENABLED)
.orElse(TransactionStatus.NOT_SUPPORTED);
Similar to #Michael's answer, you can just generate a static lookup map inside your enum which maps an enums transaction type to the actual enum:
private static final Map<String, TerminalConfigurationFeatureBitString> TRANSACTION_TYPE_TO_ENUM =
Arrays.stream(values()).collect(Collectors.toMap(
TerminalConfigurationFeatureBitString::getTransactionType,
Function.identity()
);
And then have a lookup method, also inside the enum:
public static TerminalConfigurationFeatureBitString getByTransactionType(String transactionType) {
TerminalConfigurationFeatureBitString bitString = TRANSACTION_TYPE_TO_ENUM.get(transactionType);
if(bitString == null) throw new NoSuchElementException(transactionType);
return bitString;
}
This in a way more performant than the mentioned answer, because the Map is created the first time the enum is loaded (So when it is the first time referenced). And thus the iteration happens only once. Also Maps have a rather fast lookup time so you could say that getting an enum this way works O(1) (when ignoring the initial computation time of O(n))
You can extend your enum with extra static method which will try to convert given String on enum item:
enum TerminalConfigurationFeatureBitString {
Authorize("authorize", 0), // index 0 in bit string
Authorize3d("authorize3d", 1), // index 1 in bit String
Sale("sale", 2), // index 2 in bit String
Sale3d("sale3d", 3); // index 3 in bit String
private final String value;
private final int index;
TerminalConfigurationFeatureBitString(String value, int index) {
this.value = value;
this.index = index;
}
public String getValue() {
return value;
}
public int getIndex() {
return index;
}
public static Optional<TerminalConfigurationFeatureBitString> fromValue(String value) {
for (TerminalConfigurationFeatureBitString item : values()) {
if (item.value.equals(value)) {
return Optional.of(item);
}
}
return Optional.empty();
}
}
In case option is not found, return Optional.empty(). If feature is not present it means String representation does not represent any feature. Usage:
public void test() {
EnumMap<TerminalConfigurationFeatureBitString, Boolean> featureMaps = new EnumMap<>(
TerminalConfigurationFeatureBitString.class);
Optional<TerminalConfigurationFeatureBitString> feature = TerminalConfigurationFeatureBitString.fromValue("authorize");
if (!feature.isPresent()) {
System.out.println("Feature is not foudn!");
} else {
Boolean authorize = featureMaps.get(feature.get());
if (authorize != null && authorize) {
System.out.println("Feature is enabled!");
} else {
System.out.println("Feature is disabled!");
}
}
}

Sort objects in List by String (dd/mm/yyyy)?

Can someone help me with this? I can't find any example..
I've created a Java Class called Subtaks with multiple fields. And i'm trying to sort a list of Subtasks. Firt I'm sorting the list alphabetically with one field called status; but if this field is the same i'm trying to sort it by another String field that containt a date with this format:
String resolutionDate = "18/09/2014"
I've implemented this method to sort it:
#Override
public int compareTo(Subtask o) {
if (this.status.equals(o.status))
return o.resolutiondate.compareTo(this.resolutiondate);
else
return this.status.compareTo(o.status);
}
I assume you want to sort by date so you'd either have to manually extract year, day and month and compare them or parse the date and compare the parsed dates.
For the first approach, you could use something like this:
String resolutionDateReordered = resolutionDate.replaceAll("(\\d+)/(\\d+)/(\\d+)","$3$2$1");
And then compare resolutionDateReordered. This assumes that the parts of the date have equal length, i.e. 2-digit day and month and 4 digit year and would reorder the string "18/09/2014" to "20140918", which then would be sortable by date.
This could be optimized by creating the pattern once and reusing it, e.g.
Pattern p = Pattern.compile( "(\d{2})/(\d{2})/(\d{4})" ); //pattern slightly changed to expect 2-digit day and month and 4-digit year.
and in your compare() method:
String resolutionDateReordered = p.matcher( resolutionDate ).replaceAll( "$3$2$1" );
As for the second approach you do something like this:
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
Date resolutionDateParsed = df.parse( resolutionDate );
And then compare resolutionDateParsed using Date's compareTo() method.
Depending on the size of the list, the first or second approach might be faster, but YMMV. For example, on my machine sorting a list of date strings having your format using the optimized version of the string reordering is approx. twice as fast as parsing first and comapring the dates, with 3.5 seconds vs. 7.4 seconds for 100k entries.
First you convert your String value of date("dd/mm/yyyy") to type of Date(java.util)
public class YourClassName implements Comparable<YourClassName> {
....
//return -1 for less than, 0 for equals, and 1 for more than
public compareTo(Subtask subTask) {
int result = 0;
result = getResolutionDate().compareTo(subTask.getResolutionDate());
if (result != 0)
{
return result;
}
result = getStatus().compareTo(subTask.getStatus());
if (result != 0)
{
return result;
}
...
}
}
Here is the SubTask class:
package com.test.main;
import java.util.Date;
public class Subtask implements Comparable<Subtask> {
private String status;
private Date resolutionDate;
public Subtask(String status, Date resolutionDate) {
super();
this.status = status;
this.resolutionDate = resolutionDate;
}
public String getStatus() {
return status;
}
public Date getResolutionDate() {
return resolutionDate;
}
#Override
public int compareTo(Subtask o) {
int compareToIgnoreCase = status.compareToIgnoreCase(o.getStatus());
return compareToIgnoreCase==0?resolutionDate.compareTo(o.getResolutionDate()):compareToIgnoreCase;
}
}
Main method:
public static void main(String args[]){
List<Subtask> arrayList = new ArrayList<>();
Calendar instance = Calendar.getInstance();
instance.setTime(new Date());
arrayList.add(new Subtask("test1", instance.getTime()));
instance.set(Calendar.MONTH, 11);
arrayList.add(new Subtask("test1", instance.getTime()));
instance.set(Calendar.MONTH, 5);
arrayList.add(new Subtask("test1", instance.getTime()));
Collections.sort(arrayList);
for (Subtask subtask : arrayList) {
System.out.println(subtask.getResolutionDate());
}
}

Looking to associate strings to ints in a cleaner/more efficient way

How can I improve this?
The relationship is one to one and continuous on [-1,5] so i was thinking of using enum, but I'm not sure how to compare a string value to an enum value.
If there is any better way to do this, please suggest.
Thanks!
private int evaluateWord(String sval) {
if (sval.equals("program"))
return 1;
else if (sval.equals("begin"))
return 2;
else if (sval.equals("end"))
return 3;
else if (sval.equals("int"))
return 4;
else if (sval.equals("if"))
return 5;
else
System.exit(0);
Have you considered stuffing the mapping into a HashMap once, and then just querying the map?
For example, something like this:
private static final Map<String,Integer> m_map = new HashMap<String,Integer>();
static {
m_map.put( "program", 1 );
m_map.put( "begin", 2 );
m_map.put( "end", 3 );
m_map.put( "int", 4 );
m_map.put( "if", 5 );
}
private int evaluateWord(String sval) {
Integer value = m_map.get( sval );
if ( null != value ) {
return value;
}
else {
System.exit(0);
}
}
By the way, it looks as if you're writing a parser. It can be reasonable to write a parser by hand. Another option to consider, unless you have a good reason to write it by hand, is a parser generator like ANTLR.
Using an enumeration:
enum Word {
PROGRAM(1,"program"),
BEGIN(2,"begin"),
END(3,"end"),
INT(4,"int"),
IF(5,"if");
private final int value;
private final String representation;
Word(int value, String representation)
{
this.value = value;
this.representation = representation;
}
public int value()
{ return value; }
private static Map<String, Word> fromRep =
new HashMap<String, EnumExample2.Word>();
public static Word fromRepresentation(String rep) {
if (!validRep(rep)) {
throw new IllegalArgumentException("No rep: "+rep);
}
return fromRep.get(rep);
}
public static boolean validRep(String rep)
{ return fromRep.get(rep) != null; }
static {
for (Word word : Word.values()) {
fromRep.put(word.representation, word);
}
}
}
Then your logic is:
private int evaluateWord(String sval) {
if (!Word.validRep(sval)) {
System.exit(0);
}
return Word.fromRepresentation(sval).value();
}
A hashmap could work:
private static HashMap<String, Integer> lookup = new HashMap<String, Integer>();
static {
lookup.put("program", 1);
lookup.put("being", 2);
lookup.put("end", 3);
lookup.put("int", 4);
lookup.put("if", 5);
}
private int evaluateWord(String sval) {
if ( lookup.containsKey(sval) ) {
return lookup.get(sval);
}
System.exit(0);
}
This is what a map is for;
Create a HashMap, add key and values to the map like
wordMap.put("program", Integer.valueOf(1));
....
then, to get the value do
Integer val = wordMap.get(sval);
Honestly, I wouldn't worry about keeping something like this ultra efficient, but there is a change you could make. If the word you pass is the last word you check for then your program ends up performing all of the checks in your function. This shouldn't be a problem in this case, but generally you don't want to flood your program with if statements, especially if you have a lot of cases.
Use a hashtable and just insert pairs. This way, all of your evaluateWord calls will return in amortized constant time. :)
Good luck!
Why do you need a (very subjective) "cleaner" way?
You could get more efficiency from using a hash lookup but you'd want to be certain it's called quite a bit to make the extra coding effort worthwhile. If it's something that happens infrequently (and, by that, I mean something like less than once a second), it's not worth doing (YAGNI).
One thing you might want to do for better looking code (if that's important) is to ditch the else bits, they're totally unnecessary:
private int evaluateWord(String sval) {
if (sval.equals("program")) return 1;
if (sval.equals("begin")) return 2;
if (sval.equals("end")) return 3;
if (sval.equals("int")) return 4;
if (sval.equals("if")) return 5;
System.exit(0);
}
You could just use an array or hashmap to map the enum values to the string values.
Inspired by your enum comment, I present the following. It's a bit hackish, but:
enum Word
{
PROGRAM (1), BEGIN (2), END (3), INT (4), IF (5);
public int value;
public Word (int value)
{
this.value = value;
}
};
int evaluateWord (String word)
{
return Word.valueOf(word.toUpperCase( )).value;
}
I love Java enums because you can do things like this. This is especially useful if you later want to (for example) add a unique behaviour for each word, or to maintain a long list of words. Note though that it is case insensitive.
Or, alternately:
enum Word
{
PROGRAM, BEGIN, END, INT, IF;
};
int evaluateWord (String word)
{
return Word.valueOf(word.toUpperCase( )).ordinal( ) + 1;
}

Categories