1

Hello to all java gurus from a novice java developer, help me write a sorting algorithm for an object that is nested in another object, but at the same time must take into account the field of the main object

main object:

public class Review {
    String date;
    Update update;
}

update object:

public class Update {
    String date;
}

My wrong algorithm:

public class Test {
    public static void main(String[] args) {
        Review review = new Review();
        Update update = new Update();
        update.date = LocalDateTime.now().toString();
        review.date = LocalDateTime.now().minusDays(20L).toString();
        review.update = update;

        Review review1 = new Review();
        Update update1 = new Update();
        update1.date = LocalDateTime.now().minusDays(5L).toString();
        review1.date = LocalDateTime.now().minusDays(30L).toString();
        review1.update = update1;

        Review review10 = new Review();
        Update update10 = new Update();
        update10.date = LocalDateTime.now().minusDays(1L).toString();
        review10.date = LocalDateTime.now().minusDays(100L).toString();
        review10.update = update10;

        Review review2 = new Review();
        review2.date = LocalDateTime.now().minusDays(40L).toString();

        Review review3 = new Review();
        review3.date = LocalDateTime.now().minusDays(50L).toString();

        Review review4 = new Review();
        review4.date = LocalDateTime.now().minusDays(2L).toString();

        List<Review> reviews = List.of(review, review1, review2, review3, review4, review10);

        Comparator<Review> reviewComparator = Comparator.comparing(review5 -> review5.date);

        System.out.println(reviews = reviews.stream().sorted(reviewComparator.reversed()).collect(Collectors.toList()));

        for (int i = 0; i < reviews.size(); i++) {
            Review review5 = reviews.get(i);
            for (int z = 1; z < reviews.size(); z++) {
                if (z >= i) {
                    Review review6 = reviews.get(z);
                    if (review6.update != null) {
                        LocalDateTime date1 = LocalDateTime.parse(review6.update.date);
                        LocalDateTime date2 = LocalDateTime.parse(review5.date);
                        int i1 = date1.compareTo(date2);
                        if (i1 > 0) {
                            reviews.remove(review6);
                            reviews.add(i, review6);
                        } else if (review5.update != null) {
                            LocalDateTime date3 = LocalDateTime.parse(review6.update.date);
                            LocalDateTime date4 = LocalDateTime.parse(review5.update.date);
                            int i2 = date3.compareTo(date4);
                            if (i2 > 0) {
                                reviews.remove(review6);
                                reviews.add(i, review6);
                            }
                        }
                    }
                }
            }
        }
        System.out.println(reviews);
    }
}

and result:

[
 Review{date='2022-10-11T13:17:01.754934500', update=Update{date='2023-01-18T13:17:01.754934500'}}, 
 Review{date='2022-12-20T13:17:01.754934500', update=Update{date='2023-01-14T13:17:01.754934500'}}, 
 Review{date='2022-12-30T13:17:01.754934500', update=Update{date='2023-01-19T13:17:01.753935600'}}, 
 Review{date='2023-01-17T13:17:01.754934500', update=null}, 
 Review{date='2022-12-10T13:17:01.754934500', update=null}, 
 Review{date='2022-11-30T13:17:01.754934500', update=null}
]

I expect:

[
 Review{date='2022-12-30T13:17:01.754934500', update=Update{date='2023-01-19T13:17:01.753935600'}}, 
 Review{date='2022-10-11T13:17:01.754934500', update=Update{date='2023-01-18T13:17:01.754934500'}}, 
 Review{date='2023-01-17T13:17:01.754934500', update=null}, 
 Review{date='2022-12-20T13:17:01.754934500', update=Update{date='2023-01-14T13:17:01.754934500'}}, 
 Review{date='2022-12-10T13:17:01.754934500', update=null}, 
 Review{date='2022-11-30T13:17:01.754934500', update=null}
]
5
  • you can chain comparators using Comparator.comparing(...).thenComparing(Comparator.comparing(...)) so you could implement your custom comparator and then sort using that. Commented Jan 19, 2023 at 10:42
  • custom comparator for my Review object? if I use the default comparator, it also sorts incorrectly, I also tried a chain of comparators, here it is clearly necessary to write a custom comparator, but I have not yet figured out how to do this for a nested object Commented Jan 19, 2023 at 10:47
  • Can you describe the rules for sorting? Does update.date override date in Review or is it just used for reviews that have the same date? Commented Jan 19, 2023 at 10:59
  • Side note: that reviews = reviews.stream().sorted(reviewComparator.reversed()).collect(Collectors.toList()) seems very odd and could actually easily replaced by Collections.sort(reviews, reviewComparator.reversed()) (reviews is immutable though due to your use of List.of(). To get around this try Arrays.asList(...) instead). Commented Jan 19, 2023 at 11:04
  • by default, sorting occurs by the date when the review was created, users can update the review and then the date when this review was updated is recorded there, while the date of creation of the review is always saved, for this the Update object is created, and when we output all the reviews for the user, we need to sort by date from the most recent reviews to the oldest, at the same time, if there is an Update object, take the value for sorting from it, if this object is null, use the date from the Review object Commented Jan 19, 2023 at 11:06

1 Answer 1

2

So you want to sort by Review.update.date if it exists and Review.date if it doesn't?

You can add that mapping in the key extractor function you're passing to Comparator.comparing(...):

Comparator.comparing(r -> r.update != null ? r.update.date : r.date);

Using this with Collections.sort(reviews, reviewComparator.reversed()); should result in the following list (I added my own toString to make it easier to read):

Review [date=2022-12-30T12:13:27.102934100, update=2023-01-19T12:13:27.096938200]
Review [date=2022-10-11T12:13:27.103934400, update=2023-01-18T12:13:27.103934400]
Review [date=2023-01-17T12:13:27.103934400, update=null]
Review [date=2022-12-20T12:13:27.102934100, update=2023-01-14T12:13:27.102934100]
Review [date=2022-12-10T12:13:27.103934400, update=null]
Review [date=2022-11-30T12:13:27.103934400, update=null]

Note: depending on your design it might make sense to add a method into Review that returns the last review date. I won't change too much of your code (like using proper date types) but it could look like this:

public class Review {
  String date; //creation date
  Update update;

  public String getLastReviewDate() {
    return update != null ? update.date : date;
  }
}

Then the comparator would look like this: Comparator.comparing(Review::getLastReviewDate). It's easier to read and understand that the sorting is based on the virtual property "last review date".

Sign up to request clarification or add additional context in comments.

2 Comments

Thomas, thank you so much for this decision, I didn't even think that a ternary expression could be used in the comparator
@AleksandrMartynets you can use almost anything in a comparator. In the end it comes down what you want to compare at each level, i.e. if you'd want to compare update dates first and creation dates second you'd use thenComparing() to create a chain. However, in your case update dates replace creation dates so you need to provide that "override".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.