= #JxnPortable/docs/programmer_examples/demos/AggregateOperations_Demo.jxn


! This JXN demo rephrases the examples from the Java Tutorial pages on https://docs.oracle.com/javase/tutorial/
! - https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
! - https://docs.oracle.com/javase/tutorial/collections/streams/index.html
! - https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html
! using Person.class from https://docs.oracle.com/javase/tutorial/java/javaOO/examples/Person.java
! and Averager.class from https://docs.oracle.com/javase/tutorial/collections/streams/examples/Averager.java

! Notes: 
! - As java processes lambda expressions at compile time and jxn does the same only at runtime
!   there is a certain difference in the syntax.
! - JXN does not support method references like "Person::getAge" as described in 
!   https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html.
!   As workaround use equivalent lambda expressions like "p -> p.getAge()".
! - In the following the original tutorial code is 'commented out' using the jxn special command #if false ... #endif


roster = Person.createRoster()

roster[0:].getName()
roster[0:].getGender()
roster.stream().count()



#if false  ! from https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));
#endif

_select = @JxnF( $this, "p -> isSame( p.getGender(), Person$Sex.MALE ) * ge( p.getAge(), 18 ) * le( p.getAge(), 25 )" )
_mail = @JxnF( $this, "p -> p.getEmailAddress()" )
_prn = @JxnF( $this, "email -> System.out.println( email )" )
roster.stream().filter( _select ).map( _mail ).forEach( _prn )
roster.stream().filter( _select ).map( _mail ).toArray()



#if false  ! from https://docs.oracle.com/javase/tutorial/collections/streams/index.html
roster
    .stream()
    .filter(e -> e.getGender() == Person.Sex.MALE)
    .forEach(e -> System.out.println(e.getName()));
#endif

! roster.stream().forEach( @JxnF( $this, "e -> System.out.println(e.getName())" ) )
vr = @Vector()
roster.stream().filter( @JxnF( $this, "e -> isSame( e.getGender(), Person$Sex.MALE )" ) );
$.forEach( @JxnF( $this, "e -> vr.add( e.getName() )" ) ); vr
roster.stream().filter( @JxnF( $this, "e -> isSame( e.getGender(), Person$Sex.MALE )" ) );
$.map( @JxnF( $this, "e -> e.getName()" ) ).toArray()


#if false
double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();
#endif

_isMale = @JxnF( $this, "p -> isSame( p.getGender(), Person$Sex.MALE )" )
roster.stream().filter( _isMale ).mapToInt( @JxnF( $this, "p -> p.getAge()" ) );
average = $.average().getAsDouble()



#if false  ! from https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html
Integer totalAge = roster
    .stream()
    .mapToInt(Person::getAge)
    .sum();
#endif

_age = @JxnF( $this, "p -> p.getAge()" )
totalAge = roster.stream().mapToInt( _age ).sum()


#if false
Integer totalAgeReduce = roster
   .stream()
   .map(Person::getAge)
   .reduce(
       0,
       (a, b) -> a + b);
#endif

totalAgeReduce = roster.stream().map( _age ).reduce( @Integer(0), @JxnF2( $this, "( a, b ) -> a + b" ) )


#if false
Averager averageCollect = roster.stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(Person::getAge)
    .collect(Averager::new, Averager::accept, Averager::combine);

System.out.println("Average age of male members: " +
    averageCollect.average());
#endif

_a1 = @JxnF( $this, "Averager.class.newInstance()" )
_a2 = @JxnF2( $this, "( t, u ) -> t.accept( u )" )
_a3 = @JxnF2( $this, "( t, u ) -> t.combine( u )" )
averageCollect = roster.stream().filter( _isMale ).map( _age ).collect( _a1, _a2, _a3 )
"Average age of male members: " + averageCollect.average()


#if false
List namesOfMaleMembersCollect = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(p -> p.getName())
    .collect(Collectors.toList());
#endif

namesOfMaleMembersCollect = roster.stream().filter( _isMale ).map( @JxnF( $this, "p -> p.getName()" ) ).collect( java.util.stream.Collectors.toList() )
! or (with variable $ containing the result of the last statement)
roster.stream().filter( _isMale ).map( @JxnF( $this, "p -> p.getName()" ) );
namesOfMaleMembersCollect = $.collect( java.util.stream.Collectors.toList() )


#if false
Map> byGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(Person::getGender));
#endif
            
#import java.util.stream.Collectors
byGender = roster.stream().collect( Collectors.groupingBy( @JxnF( $this, "p -> p.getGender()" ) ) )
            
            
#if false
Map> namesByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.mapping(
                    Person::getName,
                    Collectors.toList())));   
#endif

_name = @JxnF( $this, "p -> p.getName()" )
_gender = @JxnF( $this, "p -> p.getGender()" )
namesByGender = roster.stream().collect( Collectors.groupingBy( _gender, Collectors.mapping( _name, Collectors.toList() ) ) )
          
                    
#if false
Map totalAgeByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.reducing(
                    0,
                    Person::getAge,
                    Integer::sum)));
#endif

_sum = @JxnF2( $this, "( a, b ) -> a + b" )
totalAgeByGender = roster.stream().collect( Collectors.groupingBy( _gender, Collectors.reducing( @Integer(0), _age, _sum ) ) )


#if false
Map averageAgeByGender = roster
    .stream()
    .collect(
        Collectors.groupingBy(
            Person::getGender,                      
            Collectors.averagingInt(Person::getAge)));                    
#endif

averageAgeByGender = roster.stream().collect( Collectors.groupingBy( _gender, Collectors.averagingInt( _age ) ) )