Michail Michailidis
Michail Michailidis

Reputation: 12181

How to sum a list of lists per column and per row using lambda functions in Java?

I have a list of lists like that:

List<List<Wrapper>> listOfLists = new ArrayList<>();

class Wrapper {
   private int value = 0;
   public int getValue() {
      return value;
   }
}

so it looks like that:

[
[Wrapper(3), Wrapper(4), Wrapper(5)],
[Wrapper(1), Wrapper(2), Wrapper(9)],
[Wrapper(4), Wrapper(10), Wrapper(11)],
]

Is there a concise way to flatten this list of lists like below using lambda functions in Java 8:

(per column): [Wrapper(8), Wrapper(16), Wrapper(25)]
(per row): [Wrapper(12), Wrapper(12), Wrapper(25)]

potentially it could work with different sizes of internal lists:

[
[Wrapper(3), Wrapper(5)],
[Wrapper(1), Wrapper(2), Wrapper(9)],
[Wrapper(4)],
]

this would result to:

(per column): [Wrapper(8), Wrapper(7), Wrapper(9)]
(per row): [Wrapper(8), Wrapper(11), Wrapper(4)]

it seems way more complicated than: Turn a List of Lists into a List Using Lambdas and 3 ways to flatten a list of lists. Is there a reason to prefer one of them?

and what I initially did is similar to although for lists: https://stackoverflow.com/a/36878011/986160

Thanks!

Upvotes: 6

Views: 2080

Answers (5)

Adrian
Adrian

Reputation: 31

Check Matrix.straightSum(listOfLists) and Matrix.crossSum(listOfLists) methods.

class Matrix {

    public static List<Wrapper> crossSum(List<List<Wrapper>> input){
        return input.stream().reduce(Matrix::sum).orElse(Collections.emptyList());
    }

    public static List<Wrapper> straightSum(List<List<Wrapper>> input){
        return input.stream().map(Matrix::sum).collect(Collectors.toList());
    }

    private static List<Wrapper> sum(List<Wrapper> big, List<Wrapper> small){
        if (big.size() < small.size()){
            return sum(small, big);
        }
        return IntStream.range(0, big.size()).boxed()
                .map(idx -> sum(big, small, idx))
                .collect(Collectors.toList());
    }

    private static Wrapper sum(List<Wrapper> big, List<Wrapper> small, int idx){
        if (idx < small.size()){
            return new Wrapper(big.get(idx).getValue() + small.get(idx).getValue());
        }
        return big.get(idx);
    }

    private static Wrapper sum(List<Wrapper> line){
        return new Wrapper(line.stream().mapToInt(Wrapper::getValue).sum());
    }
}

Upvotes: 2

fps
fps

Reputation: 34460

If instead of a List<List<Wrapper>> you had a Guava Table, the solution would be a breeze:

Table<Integer, Integer, Wrapper> table = HashBasedTable.create();

table.put(0, 0, new Wrapper(3));
table.put(0, 1, new Wrapper(5));
// TODO fill the table 

List<Wrapper> byRow = table.rowMap().values().stream()
    .map(row -> row.values().stream().mapToInt(Wrapper::getValue).sum())
    .map(Wrapper::new)
    .collect(Collectors.toList());

List<Wrapper> byColumn = table.columnMap().values().stream()
    .map(column -> column.values().stream().mapToInt(Wrapper::getValue).sum())
    .map(Wrapper::new)
    .collect(Collectors.toList());

This code assumes that Wrapper has a constructor that accepts the int value as an argument. Please see Table.rowMap and Table.columnMap docs for further details.

Upvotes: 3

Mahendra
Mahendra

Reputation: 1426

My two cents to the solution with a custom function List sum(List,List)

static class Wrapper
{
    public int value = 0;

    public Wrapper( int value )
    {
        super();
        this.value = value;
    }

    public int getValue()
    {
        return value;
    }

    public void setValue( int value )
    {
        this.value = value;
    }

    @Override
    public String toString()
    {
        return "Wrapper [value=" + value + "]";
    }

}

public static List<Wrapper> sum( List<Wrapper> l1, List<Wrapper> l2 )
{
    int l1Size = l1.size();
    int l2Size = l2.size();
    int size = Math.max( l1.size(), l2.size() );
    List<Wrapper> result = new ArrayList<>( size );
    int v1 = 0;
    int v2 = 0;
    for ( int i = 0; i < size; i++ )
    {
        v1 = i < l1Size && l1.get( i ) != null ? l1.get( i ).getValue() : 0;
        v2 = i < l2Size && l2.get( i ) != null ? l2.get( i ).getValue() : 0;
        result.add( new Wrapper( v1 + v2 ) );
    }
    return result;
}

public static void main( String[] args )
    throws Exception
{

    List<List<Wrapper>> listOfLists = new ArrayList<>();
    List<Wrapper> list1 = new ArrayList<>();
    list1.add( new Wrapper( 3 ) );
    list1.add( new Wrapper( 4 ) );
    list1.add( new Wrapper( 5 ) );

    List<Wrapper> list2 = new ArrayList<>();
    list2.add( new Wrapper( 1 ) );
    list2.add( new Wrapper( 2 ) );
    list2.add( new Wrapper( 9 ) );

    List<Wrapper> list3 = new ArrayList<>();
    list3.add( new Wrapper( 4 ) );
    list3.add( new Wrapper( 10 ) );
    list3.add( new Wrapper( 11 ) );

    listOfLists.add( list1 );
    listOfLists.add( list2 );
    listOfLists.add( list3 );

    List<Wrapper> colSum = listOfLists.stream().reduce( Collections.emptyList(), ( l1, l2 ) -> sum( l1, l2 ) );

    List<Wrapper> rowSum =
        listOfLists.stream().map( list -> list.stream().reduce( new Wrapper( 0 ),
                                                                ( w1, w2 ) -> new Wrapper( w1.value
                                                                    + w2.value ) ) ).collect( Collectors.toList() );
    System.out.println( "Sum by column : " );
    colSum.forEach( System.out::println );

    System.out.println( "Sum by row : " );
    rowSum.forEach( System.out::println );

}

Upvotes: 1

Naman
Naman

Reputation: 31898

One liners if they might help (wanted to share as comment, too long a text) for List<Wrapper> type of resultPerRow and resultPerColumn :

IntStream.range(0, listOfLists.size()).forEach(
            i -> resultPerRow.add(new Wrapper(listOfLists.get(i).stream().mapToInt(Wrapper::getValue).sum())));

IntStream.range(0, listOfLists.stream().mapToInt(List::size).max().orElse(0))
            .forEach(i -> resultPerColumn.add(new Wrapper(IntStream.range(0, listOfLists.size())
                    .map(y -> listOfLists.get(y).size() < y ? 0 : listOfLists.get(y).get(i).getValue())
                    .sum())));

Upvotes: 2

Eugene
Eugene

Reputation: 120858

Per row is actually pretty easy:

List<Wrapper> perRow = listOfLists.stream()
            .map(x -> x.stream().mapToInt(Wrapper::getValue).sum())
            .map(Wrapper::new)
            .collect(Collectors.toList());

Per column on the other hand is not that trivial:

private static List<Wrapper> perColumn(List<List<Wrapper>> listOfList) {
    int depth = listOfList.size();
    int max = listOfList.stream().map(List::size).max(Comparator.naturalOrder()).get();
    return IntStream.range(0, max)
            .map(x -> IntStream.range(0, depth)
                    .map(y -> listOfList.get(y).size() < y ? 0 : listOfList.get(y).get(x).getValue())
                    .sum())
            .mapToObj(Wrapper::new)
            .collect(Collectors.toList());
}

Upvotes: 7

Related Questions