Forum Stats

  • 3,722,780 Users
  • 2,244,412 Discussions
  • 7,850,084 Comments

Discussions

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Filter list of Objs with internal dependency

vcandela
vcandela Member Posts: 5
edited December 2016 in Java Lambda MOOC

Hi all,

I'm trying to solve in an elegant functional way the following:

Starting from a List<MyObj> list where MyObj has as properties id, Status (enum), duration, parentId I'd like to filter this list with only MyObjs with parentId = null (so only fathers) with child (child_parentId = father_id in one-to-one relationship) having status = Completed and duration (long) > 20L.

e.g.
initial list
MyObjFather1: id=f1, Status.Completed, 40L, null

MyObjChild1: id=c1, Status.Completed, 25L, f1

MyObjFather2: id=f2, Status.Completed, 10L, null

MyObjChild2: id=c2, Status.Completed, 10L, f2

return list

MyObjFather1: id=f1, Status.Completed, 40L, null

It's pretty simple in "normal" Java but I'd like to use the functional approach

Any suggestions

Answers

  • Guadalupe Estrada Flores
    Guadalupe Estrada Flores Member Posts: 2
    edited December 2016

    You could do something like this:

        

            List<MyObj> initialList; // Your list

        

            Observable<List<MyObj>> finalListObs = Observable.from(initialList)

                    .filter(element -> {

                        if(element.getString("parentId") == null)

                            return true;

                        else

                            return false;

                    })

                    .filter(element -> {

                        if(element.getString("status").equals("Completed"))

                            return true;

                        else

                            return false;

                    })

                    .filter(element -> {

                        if(element.getLong("duration") > 20L)

                            return true;

                        else

                            return false;

                    })

                    .toList();

            finalListObs.subscribe(System.out::print); // This would print the resulting list of element f1

    The if-else blocks are just to make code more clear, but you could replace them with a simple: return (expression).

    Another way would be as simple as this:

    List<JsonObject> newList = new ArrayList<JsonObject>();

            initialList.forEach(elem -> {

                if(elem.getString("parentId") == null &&

                        elem.getString("status").equals("Completed") &&

                            elem.getLong("duration") > 20L)

                    newList.add(elem);

            });

    Hope that helps.

    Best regards, Lupita.

  • vcandela
    vcandela Member Posts: 5
    edited December 2016

    Thanks Lupita for your snippet but it doesn't solve the problem.

    I need only the Fathers (objs with parentId = null) only in case their child (objs with parentId = id of the parent) has status = Completed AND duration > 20L

    In regular Java I'd split the list in 2 different maps<String, Obj>, FathersMap and ChildsMaps, using the obj.getId as key. Looping the ChildsMap in case the conditions, status = Completed && duration > 20L, are satisfied add into the finalList the element from the FathersMap having the id = child.getParentId

    Many thanks

  • Guadalupe Estrada Flores
    Guadalupe Estrada Flores Member Posts: 2
    edited December 2016

    Oh I hadn't understand what you meant before...

    How about this?

            MyObj parent = null;

            List<MyObj> initialList; // Your list

            List<MyObj> children = new ArrayList<MyObj>();

            initialList.forEach(elem -> {

                if(elem.getString("parentId") != null)

                    children.add(elem);                       

            });

       

            Observable<List<MyObj>> finalListObs = Observable.from(initialList)

                    .filter(element -> element.getString("parentId") == null)

                    .map(element -> {

                        parent = null;

                        children.forEach(child -> {

                            String child_father = child.getString("parentId");

                            if(child_father.equals(element.getString("id"))){

                                if(child.getString("status").equals("Completed")

                                        && child.getLong("duration") > 20L)

                                    parent = element;

                            }

                        });

                        return parent;

                    })

                    .filter(parent -> parent != null)

                    .toList();

            finalListObs.subscribe(System.out::println);

    Give it a try, hope it helps.

  • vcandela
    vcandela Member Posts: 5
    edited December 2016

    Hi @Guadalupe Estrada Flores, thanks again.

    Trying to avoid the foreach as suggest in the MOOC.

    What about this solution:

    public class Functional {

       private static Call callF1 = new Call("idF1", null, Call.Status.Completed, 30L);

       private static Call callC1 = new Call("idC1", "idF1", Call.Status.Completed, 20L); //not-valid

       private static Call callF2 = new Call("idF2", null, Call.Status.Completed, 35L);

       private static Call callC2 = new Call("idC2", "idF2", Call.Status.Completed, 25L); //valid

       private static Call callF3 = new Call("idF3", null, Call.Status.Completed, 25L);

       private static Call callC3 = new Call("idC3", "idF3", Call.Status.Completed, 15L); //not-valid

       private static Call callF4 = new Call("idF4", null, Call.Status.Completed, 50L);

       private static Call callC4 = new Call("idC4", "idF4", Call.Status.Completed, 40L); //valid

       private static Call callF5 = new Call("idF5", null, Call.Status.Completed, 20L);

       private static Call callC5 = new Call("idC5", "idF5", Call.Status.Completed, 10L); //not-valid

       private static Call callF6 = new Call("idF6", null, Call.Status.Completed, 60L);

       private static Call callC6 = new Call("idC6", "idF6", Call.Status.NotCompleted, 50L); //not-valid

       public static void main(String... args) {

      List<Call> initialList = Arrays.asList(callC1, callC2, callC3, callC4, callC5, callC6, callF1, callF2, callF3, callF4, callF5, callF6);

      Predicate<Call> validChildPredicate = call -> call.getParentId() != null && call.getStatus().equals(Call.Status.Completed) && call.getDuration() > 20L;

      List<String> fathersId = initialList.stream().filter(validChildPredicate).map(Call::getParentId).peek(System.out::println).collect(Collectors.toList());

      Predicate<Call> validFatherPredicate = call -> call.getParentId() == null && call.getStatus().equals(Call.Status.Completed);

      List<Call> finalList = initialList.stream()

      .filter(father -> fathersId.stream().anyMatch(id -> id.equals(father.getsId())))

      .filter(validFatherPredicate)

      .peek(System.out::println)

      .collect(Collectors.toList());

      }

    }

    class Call {

       public Call(String sId, String parentId, Status status, Long duration) {

       this.sId = sId;

       this.parentId = parentId;

       this.status = status;

       this.duration = duration;

      }

       enum Status {

       Completed,

       NotCompleted
       }

       private String sId;

       private String parentId;

       private Status status;

       private Long duration;

       //getters/setters

       @Override
       public String toString() {

       return "Call{" +

       "sId='" + sId + '\'' +

       ", parentId='" + parentId + '\'' +

       ", status=" + status +

       ", duration=" + duration +

       '}';

      }

    }

    Result:

    idF2

    idF4

    Call{sId='idF2', parentId='null', status=Completed, duration=35}

    Call{sId='idF4', parentId='null', status=Completed, duration=50}

Sign In or Register to comment.