Intro
I will start off using an SQL Approach, and follow on later with Java.
(SQL is my love).
SQL Approach
I don't really know if it could be performed by a sql request, hope someone could provide me the solution.
This could be solved using an SQL approach. Here is a first non-optimized example:
Schema
create table Activity(
name varchar(2),
modificationDate int,
parentActivity varchar(2)
);
insert into activity values('E1', 1, 'P1' );
insert into activity values('P2', 1, null );
insert into activity values('P1', 1, null );
insert into activity values('P3', 2, null );
insert into activity values('E1', 10, 'P1' );
Query (Version 1 Toy Example)
SELECT *
FROM
( SELECT a1.*,
(SELECT max(modificationDate)
FROM Activity a2
GROUP BY a2.parentActivity
HAVING a2.parentActivity = a1.name) AS childMod,
1 AS isParent
FROM Activity a1
WHERE a1.parentActivity IS NULL
UNION SELECT a3.*,
(SELECT max(modificationDate)
FROM Activity a4
GROUP BY a4.name
HAVING a3.name = a4.name), 0 AS isParent
FROM Activity a3
WHERE a3.parentActivity IS NOT NULL ) AS TEMP
ORDER BY childMod DESC,
isParent DESC,
temp.modificationDate DESC;
The above can be improved by removing some of the inefficient sub queriess and instead using joined sub queries (aliased temp table) and/or unary self joins.
Result: http://i.prntscr.com/hnTeXtQzR5_BRui3TXJFSw.png
Query (Final Version 2 Toy Example)
select
*
FROM
(
select name, modificationDate, childMaxMod, 1 as isParent from
Activity a1 left outer join
(select ParentActivity, max(modificationDate) as childMaxMod from Activity group by ParentActivity) as a2
on a1.name = a2.parentActivity
WHERE a1.parentActivity IS NULL
UNION
select name, modificationDate, childMaxMod, 0 as isParent from
Activity a1 inner join
(select ParentActivity, max(modificationDate) as childMaxMod from Activity group by ParentActivity) as a2
on a1.parentActivity = a2.parentActivity
) as Temp
order by childMaxMod desc, isParent desc, modificationDate desc;
Java Approach
Luca's solution is quite good and is purely a java 8 functional solution.
I post my own as an alternative but it makes use of imperative java programming.
Activity Class
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Builder
@Data
@AllArgsConstructor
class Activity {
long modificationDate;
String name;
Activity parentActivity;
Set<Activity> subActivities = new HashSet();
boolean active;
public String debugString;
public int hashCode(){
return Objects.hash(modificationDate, name, subActivities, active);
}
}
Code - Setup Section
public static void main(String[] args) {
//SpringApplication.run(MneServiceApplication.class, args);
ArrayList<Activity> set = new ArrayList<Activity>();
Activity p1 = Activity.builder().modificationDate(0).name("P1").subActivities(new HashSet<>()).build();
Activity e1 = Activity.builder().modificationDate(10).name("E1").subActivities(new HashSet<>()).build();
Activity e1_2 = Activity.builder().modificationDate(01).name("E1").subActivities(new HashSet<>()).build();
Activity p3 = Activity.builder().modificationDate(02).name("P3").subActivities(new HashSet<>()).build();
Activity p2 = Activity.builder().modificationDate(01).name("P2").subActivities(new HashSet<>()).build();
p1.getSubActivities().add(e1);
p1.getSubActivities().add(e1_2);
e1.setParentActivity(p1);
e1_2.setParentActivity(p1);
set.add(p1);
set.add(e1);
set.add(e1_2);
set.add(p3);
set.add(p2);
Code - Logic (Continuation)
Set<Activity> actives = set.stream()
.sorted(Comparator.comparing(Activity::getModificationDate).reversed())
.collect(Collectors.toCollection(LinkedHashSet::new));
HashMap<Activity, Optional> maxChildModifications = new HashMap<Activity, Optional>();
actives = actives.stream().sorted((Activity a, Activity b) -> {
DecimalFormat decimalFormat = new DecimalFormat("0000000000000000");
String comparisonValue_A = getComparisonString2(maxChildModifications, a, decimalFormat);
String comparisonValue_B = getComparisonString2(maxChildModifications, b, decimalFormat);
a.debugString = comparisonValue_A;
b.debugString = comparisonValue_B;
return comparisonValue_B.compareTo(comparisonValue_A);
}).collect(Collectors.toCollection(LinkedHashSet::new));
for(Activity activity:actives){
System.out.println(activity.getName() + " " + activity.getDebugString());
}
}
private static String getComparisonString2(HashMap<Activity, Optional> maxChildModificationsLookup, Activity activity, DecimalFormat decimalFormat) {
//Is a Parent or a Child
Activity lookupActivity = activity.getParentActivity();;
if(lookupActivity == null){ //It is a parent
lookupActivity = activity;
}
String comparisonValue_B = "";
Long isParent_B = 0L;
Long maxChildMod_B = 0L;
Optional<Activity> maxChildActivityOptional_B = getMaxChildModActivity(lookupActivity, maxChildModificationsLookup);
if(maxChildActivityOptional_B.isPresent()) {
maxChildMod_B = maxChildActivityOptional_B.get().getModificationDate();
}
if(activity.getSubActivities().size() > 0){
isParent_B = 1L; //Or use some other comparison system than the concatenated string
}else{
isParent_B = 0L;
}
comparisonValue_B = String.valueOf( decimalFormat.format(maxChildMod_B)).concat("-").concat(String.valueOf(isParent_B));
comparisonValue_B = comparisonValue_B .concat("-").concat(decimalFormat.format(activity.getModificationDate()));
return comparisonValue_B;
}
private static Optional<Activity> getMaxChildModActivity(Activity a, HashMap<Activity, Optional> maxChildModificationsLookup) {
Optional<Activity> result = maxChildModificationsLookup.get(a);
if(result!=null){
return result;
}
result = a.getSubActivities().stream().max((Activity a2, Activity b2) ->
{
return new Long(a2.getModificationDate()).compareTo(b2.getModificationDate());
}
);
maxChildModificationsLookup.put(a, result);
return result;
}
Result:
P1 0000000000000010-1-0000000000000000
E1 0000000000000010-0-0000000000000010
E1 0000000000000010-0-0000000000000001
P3 0000000000000000-0-0000000000000002
P2 0000000000000000-0-0000000000000001
Clearly my SQL solution, and Luca's Java 8 solutions are more more concise and elegant than the above java implementation (though some formatting changes might be needed for readability).
Comparatorwhich will sort the list according to the requested value for you. Comparator