I have a List<String> that contains a list of times from 8:00 am to 4:00 pm.
When I show it in output it appears unsorted, and when I use Collections.sort(myList); it sorts it as from 1:00 pm to 8:00 am.
How could I sort my list from 8:00am to 4:00pm ?
Don't reinvent the wheel, use collection (or Lambdas if java8 is allowed)
How??:
keep the list as strings, but use an Anonymous comparator, in there, parse the string to dates, compare them and there you have it.
here a snippet:
List<String> l = new ArrayList<String>();
l.add("8:00 am");
l.add("8:32 am");
l.add("8:10 am");
l.add("1:00 pm");
l.add("3:00 pm");
l.add("2:00 pm");
Collections.sort(l, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
try {
return new SimpleDateFormat("hh:mm a").parse(o1).compareTo(new SimpleDateFormat("hh:mm a").parse(o2));
} catch (ParseException e) {
return 0;
}
}
});
System.out.println(l);
Convert your Strings to Dates and sort the dates. In Java in general you should always handle the objects as they are, not Strings. Strings are names or other texts, when there is a special meaning of a String you better convert into that meaningful object.
Here is a short program for what you want to do.
public static void main(String[] args) throws ParseException {
ArrayList<String> times = new ArrayList<String>();
times.add("8:00 pm");
times.add("8:00 am");
times.add("7:00 pm");
times.add("7:00 am");
ArrayList<Date> dates = new ArrayList<Date>();
DateFormat format = new SimpleDateFormat("h:m a", Locale.ENGLISH);
for(String time : times){
dates.add(format.parse(time));
}
Collections.sort(dates);
System.out.println(dates);
}
Steps:
Convert each string to a standardized Date
Sort using Collections.sort()
DetailsVOTemp model has time and add set-get method and I already have a list which is list with name of list.
Get the time and latlong from the list and store in two arraylist than finally sort it
List< DetailsVOTemp> firstlist = new ArrayList<>();
List<DetailsVOTemp> secondlisr = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
DetailsVOTemp mDetailsVOTemp = new DetailsVOTemp();
mDetailsVOTemp.setTime(list.get(i).getTime());
mDetailsVOTemp.setAdd(list.get(i).getLatLong());
mVoTemps.add(mDetailsVOTemp);
}
for (int i = 0; i < list.size(); i++) {
DetailsVOTemp mDetailsVOTemp = new DetailsVOTemp();
mDetailsVOTemp.setTime(list.get(i).getDropOffTime());
mDetailsVOTemp.setAdd(list.get(i).getDropLatLong());
mVoTemps1.add(mDetailsVOTemp);
}
mVoTemps.addAll(mVoTemps1);
Collections.sort(mVoTemps, new Comparator<DetailsVOTemp>() {
#Override
public int compare(DetailsVOTemp o1, DetailsVOTemp o2) {
return o1.getTime().compareTo(o2.getTime());
}
});
StringBuilder mBuilder = new StringBuilder();
StringBuilder mBuilder1 = new StringBuilder();
for (int i = 0; i < mVoTemps.size(); i++) {
mBuilder1.append(mVoTemps.get(i).getTime() +" :: ");
mBuilder.append(mVoTemps.get(i).getAdd() +" :: ");
}
Log.d("Time : ", mBuilder1.toString());
Log.d("Address : ", mBuilder.toString());
Use 24 hour time format in your list and then sort it.
You can revert it back to AM and PM anytime.
Like others I recommend not reinventing the wheel, and I also recommend discarding the old and outdated classes SimpleDateFormat and Date. We have so much better these days.
Given
static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
we can do
Collections.sort(listOfTimeStrings,
Comparator.comparing(
(String s) -> LocalTime.parse(s.toUpperCase(), dtf)));
This will work nicely for a few hundred strings, maybe more. It will, however, parse each string each time two strings are to be compared. That could be many times. If you want to parse each string only once, instead do:
List<String> sortedList = listOfTimeStrings.stream()
.map(String::toUpperCase)
.map(s -> LocalTime.parse(s, dtf))
.sorted()
.map(dtf::format)
.map(String::toLowerCase)
.collect(Collectors.toList());
I have taken it literally when you give am and pm in lowercase, so I am converting to uppercase before the parsing and back to lowercase after formatting. Depending on your data you may skip these steps.
Related
I have a list of LocalDates lets say
14-06-2020, 15-06-2020, 17-06-2020, 19-06-2020, 20-06-2020, 21-06-2020
and I want to have all consecutive intervals from above dates. So the output would be like
Interval 1 = [14-06-2020, 15-06-2020]
Interval 2 = [17-06-2020, 17-06-2020]
Interval 3 = [19-06-2020, 21-06-2020]
What would be the most efficient way to do in Java
So I have create an Interval class that would hold start and enddate
public class Interval{
private LocalDate startDate;
private LocalDate endDate;
}
I can iterate over each element in list of dates and then check for logic if two dates are consecutive something in below line,
public static void main(String args[]){
List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.of(2020,6,14));
dates.add(LocalDate.of(2020,6,15));
dates.add(LocalDate.of(2020,6,17));
dates.add(LocalDate.of(2020,6,19));
dates.add(LocalDate.of(2020,6,21));
dates.add(LocalDate.of(2020,6,20));
Collections.sort(dates);
//Handle if empty or null
List<Interval> intervals = new ArrayList<>();
if(dates==null || dates.size()==0){
throw new IllegalArgumentException("list cannot be empty");
}
//If only one date then the interval starts and ends with same date
if(dates.size()==1){
Interval interval = new Interval();
interval.setStartDate(dates.get(0));
interval.setEndDate(dates.get(0));
}
LocalDate firstDate = dates.get(0);
for(int i =1;i<dates.size(); i++){
LocalDate endDate = dates.get(i);
LocalDate nextDate = endDate.plusDays(1);
//iterate over to get the desired list of interval
while(my condition satisfies){
//create new interval
}
//intervals.add(interval
}
}
I wanted to check if there is something better using stream api or can i group dates by consecutive days and then collect them using interval
A "workaround" towards an approach based on Stream upon the sorted collection could be to use markers for range lookup -
List<Integer> rangeMarkers = new ArrayList<>();
rangeMarkers.add(0);
rangeMarkers.addAll(IntStream.range(0, dates.size() - 1)
.filter(i -> !dates.get(i).plusDays(1).equals(dates.get(i + 1)))
.mapToObj(i -> i + 1)
.collect(Collectors.toList()));
rangeMarkers.add(dates.size());
System.out.println(rangeMarkers);
and then use those markers to map dates to Interval -
List<Interval> intervals = IntStream.range(0, rangeMarkers.size() - 1)
.mapToObj(i -> new Interval(dates.get(rangeMarkers.get(i)),
dates.get(rangeMarkers.get(i + 1) - 1)))
.collect(Collectors.toList());
System.out.println(intervals);
Stream API isn't your friend in this case. you can do it as I displayed the below but I think it isn't readable.
without non-stream API makes it more readable.
After sorting the list loop over the list by skipping the first index. current localDate is the first element in the list. by checking its equality with the other elements change the current value and merge intervalMap as you see. for current localDate in the loop if equality isn't matched put it on the map with a new key. (++index). because you just want to have start localDate and end localDate in the merge function I just set the endLocalDate of the first interVal with the second interval endLocalDate value.
LocalDate current = dates.get(0);
Map<Integer, Interval> intervalMap2 = new HashMap<>();
int index = 1;
intervalMap2.put(1, new Interval(current,current));
for (LocalDate localDate : dates.subList(1, dates.size())) {
if (current.plusDays(1).equals(localDate)) {
current = localDate;
intervalMap2.merge(index, new Interval(localDate,localDate),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
} else {
intervalMap2.merge(index, new Interval(current,current),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
intervalMap2.put(++index, new Interval(localDate,localDate));
current = localDate;
}
}
however, if you interested in do it with stream version you can do like:
Map<Integer, Interval> intervalMap = dates.stream().sorted()
.collect(HashMap::new, (hashMap, localDate) -> {
if (hashMap.get(hashMap.size()) != null &&
hashMap.get(hashMap.size()).getEndDate()
.equals(localDate.minusDays(1))) {
hashMap.merge(hashMap.size(),
new Interval(localDate, localDate),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
} else {
if (hashMap.size() > 1)
hashMap.merge(hashMap.size(), hashMap.get(hashMap.size()),
(val, val2) -> { val.setEndDate(val2.getEndDate());return val;});
hashMap.put(hashMap.size() + 1, new Interval(localDate, localDate));
}
}, HashMap::putAll);
...
public class DateLimiter {
private Date startDate;
private Date endDate;
}
List<DateLimiter> period = new ArrayList<>();
period.add(1/1/2020, 31/1/2020);
period.add(1/2/2020, 29/2/2020);
period.add(1/3/2020, 1/5/2020);
period.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));
...
How to use java 8 (LAMBDA) to check the collection if all the periods object are continuous periods
for example endDate+1 "day" should be equals to the next period startDate
You are doing a number of things incorrectly or not at all.
Trying to add a non-string to an ArrayList that only takes DateLimiter objects.
Trying to add two elements to an ArrayList with add.
Not creating instances of DateLimiter.
You should also be using LocalDate and not Date.
Given that you must use java.util.Date and the list is sorted, you can use the function below.
public boolean isCorrect(ArrayList<DateLimiter> list) {
for (int i = 1; i < list.size(); i++) {
Date prevEndDate = list.get(i - 1).getEndDate();
Date currStartDate = list.get(i).getStartDate();
Calendar cal = Calendar.getInstance();
cal.setTime(prevEndDate);
cal.add(Calendar.DAY_OF_MONTH, 1);
if (cal.getTime().equals(currStartDate)) {
return false;
}
}
return true;
}
I have an Array of Hashmap and each hashmap contain 24 hour time as key-value pair.
I want to sort this array in ascending order of time. how can i achieve this?
here is snippet of my code:
HashMap[] arr = new HashMap[100];
for(int i=0;i<100;i++) {
HashMap<String,String> child=new HashMap<String,String>();
child.put("some_time","21:09"); //time changes per iteration(time is in 24-hour format)
arr[i]=child;
}
Here is the full code that will sort the array on time which is in hh:mm format:
HashMap<String,String>[] harr = new HashMap[10];
final DateFormat df = new SimpleDateFormat("kk:mm");
// prepare your data
for(int i=0;i<harr.length;i++) {
HashMap<String,String> child=new HashMap<String,String>();
int ss = (int)(Math.random() * (59 + 1));
//time changes per iteration(time is in 24-hour format)
child.put("some_time", String.format("21:%02d", ss));
harr[i]=child;
}
System.out.printf("map array is: %s%n", Arrays.deepToString(harr));
// now apply sort using a custom method
Arrays.sort(harr, new Comparator<HashMap<String,String>>() {
public int compare(HashMap<String,String> o1, HashMap<String,String> o2) {
String t1 = o1.get("some_time");
String t2 = o2.get("some_time");
try {
Date dt1 = df.parse(t1);
Date dt2 = df.parse(t2);
return dt1.compareTo(dt2);
} catch (ParseException e) {
e.printStackTrace();
}
return 0;
}
});
System.out.printf("sorted map array is: %s%n", Arrays.deepToString(harr));
You can use Arrays.sort(T[], Comparator<T>). This allows you to pass an array of any type and write your own custom comparator method like this:
Arrays.sort(arr, new Comparator<HashMap>() {
public int compare(HashMap o1, HashMap o2) {
// Compare values you're interested in and return int as specified by Comparator API
}
});
See the API for details on what to return.
Before proceeding with this approach, do think about the comments and decide whether the array of hashmaps is the right way to go. If, as I pointed out, you have a bunch of maps, each containing large amounts of information, with one entry being your dates, then this may be the right thing to do, in which case the easiest way to sort the array would be to use Arrays.sort method:
HashMap[] arr=new Hashmap[100];
for(int i=0;i<100;i++){
HashMap<String,String> child=new HashMap<String,String>();
... // put all the info into the HashMap
child.put("some_time","21:09"); //time changes per iteration(time is in 24-hour format)
arr[i]=child;
}
Arrays.sort(arr, new Comparator<HashMap>() {
public int compare(HashMap o1, HashMap o2) {
String d1 = o1.get("some_time");
String d2 = o2.get("some_time");
//compare the two dates. If you're always in the same format, e.g. HH:MM (24 hours, two-digit hour, two-digit year), you might even be able to simply compare strings:
return d1.compareTo(d2);
}
});
The general approach is to write a Comparator to order a pair of your HashMap objects based on the key, and then pass that as a parameter to the Arrays.sort(T[], Comparator<T>) method.
Th comparator would look something like this:
Comparator<HashMap> DATE_ORDER = new Comparator<HashMap>() {
public int compare(Comparator<HashMap>h1, Comparator<HashMap>h2) {
String time1 = h1.get("some_time");
String time2 = h2.get("some_time");
return time1.compareTo(time2); // assuming that the time strings
// can be ordered that way
}
};
Having said that, your problem has the "smell" of trying to use Maps when they should really be writing custom classes.
As Bhavik points out, you may not be using the JDK to it's full potential - have a look at SortedMap which may be just what you're looking for; possibly with your own implementation of a Comparator.
SortedMap arr = new TreeMap<String,HashMap<String,String>>();
for ( int i=0 ; i<100 ; i++ )
{
Map<String,String> child = HashMap<String,String>();
child.put( "some_time" , "21:09" );
arr.put( "21:09" , child );
}
then you can use arr.values().iterator() to get your sorted children.
Cheers,
I have an ArrayList which contains dates with a format (Satuday,4 Februray 2012). How can I sort this ArrayList ?
This is one of the Simplest way to sort,
Collections.sort(<Your Array List>);
If you have any special requirements while sorting, so you may do it by providing your own Comparator. For example:
//your List
ArrayList<Date> d = new ArrayList<Date>();
//Sorting
Collections.sort(d, new Comparator<Date>() {
#Override
public int compare(Date lhs, Date rhs) {
if (lhs.getTime() < rhs.getTime())
return -1;
else if (lhs.getTime() == rhs.getTime())
return 0;
else
return 1;
}
});
The key element is that you are converting your Date object into milliseconds (using getTime()) for comparison.
After 8 years..
List<Date> dates = datelist;
List<Date> sorted = dates.stream()
.sorted(Comparator.comparingLong(Date::getTime))
.collect(Collectors.toList());
I want to sort my ArrayList by Date.
My ArrayList as:
10 June - name
15 April - name
23 July - name
03 March - name
It has day, month and string name. How do I sort by Date?
Thanks
As pointed out by #The Elite Gentleman, you should use a custom comparator. Here's a complete example:
import java.text.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<String>(
Arrays.asList("10 June - name",
"15 April - name",
"23 July - name",
"03 March - name"));
// Print list before sorting.
System.out.println(list);
// Sort list according to date.
Collections.sort(list, new Comparator<String>() {
DateFormat df = new SimpleDateFormat("dd MMM");
#Override
public int compare(String s1, String s2) {
try {
Date d1 = df.parse(s1.split("-")[0].trim());
Date d2 = df.parse(s2.split("-")[0].trim());
return d1.compareTo(d2);
} catch (ParseException pe) {
System.out.println("erro: " + pe);
return 0;
}
}
});
// Print list after sorting
System.out.println(list);
}
}
Output:
[10 June - name, 15 April - name, 23 July - name, 03 March - name]
[03 March - name, 15 April - name, 10 June - name, 23 July - name]
(A better idea may however be encapsulate the date/name pairs into a separate class. This would avoid the need to parse the string each time it needs to be used for these type of purposes.)
You can write your own date Comparator and pass sort it using Collections.sort(List<T>, Comparator<? super T> c); method.
Look at Comparator and Comparable.
In the Collections class there is a static method called sort(...) where you can pass a list and a comparator.
Create a comparator that compares the values from the list and return the appropriate value.
Here you an example:
List<Date> dates=new ArrayList<Date>();
dates.add(new Date(System.currentTimeMillis()));
dates.add(new Date(System.currentTimeMillis()+231892738));
dates.add(new Date(System.currentTimeMillis()-742367634));
for(Date d:dates)
System.out.println(d);
Collections.sort(dates);
for(Date d:dates)
System.out.println(d);
Or you can use custom comparator
Collections.sort(dates,customComparator);
Try using the Collections.sort method for your case:
Collections.sort(myList, new Comparator<String>(){
#Override
public int compare(String s1, String s2) {
// perform the comparison here and returns a negative integer,
// zero, or a positive integer as the first argument is less than,
// equal to, or greater than the second
}
});
create a function which turns that date string into an int?
i.e.
str2int("name 10 June") => 610
str2int("name 15 Jan") => 115
int str2int(String s){
String[] elms = s.split("\\s+");
// ignore first argument which is name
int day = Integer.parseInt(elms[1]);
int month = month2int(elms[2]);
return day + month;
}
// month2int("Jan") => 100
// month2int("Feb") => 200
// .
// .
// month2int("Dec") => 1200
// you get the idea :-)
use a comparator which compares those Strings like that..