N. Pat.
N. Pat.

Reputation: 53

Insert items from one collection to another by condition and iterate over both collections using Google Guava or java 8 Stream API

I have a specific case for two collections of such items:

public class Item {
    private Long id;
    private boolean isVisible;
}

List A contains items both visible and invisible.

List B contains only visible items from list A, but ordered (indexed) differently.

I need a result list with visible items layed down in relative order of B, and with unchanged relative order of invisible items from A. So far, the only way I could do it right is through iterators:

Iterator<Item> ai = A.listIterator();
Iterator<Item> bi = B.iterator();
while(ai.hasNext() && bi.hasNext()) {
    Item next = ai.next();
    if (next.isVisible() && B.contains(next)) {
        ai.set(bi.next())
    }
}

So we're replacing every next visible item in A contained in B, with item from B. I wonder if there is more beautiful solution to this problem through either Guava or Stream API.

Upvotes: 2

Views: 105

Answers (3)

Angel Koh
Angel Koh

Reputation: 13505

If you need to share the same object between listA and listB, then you can do the following

loop through listA,
- if a is invisible, return a,
- if a is visible, return b.
then combine back into a list and replace the list (or create a new list if needed).

    AtomicInteger index = new AtomicInteger();
    listA = listA.stream()
            .map(a -> a.isVisible ? listB.get(index.getAndIncrement()) :  a)
            .collect(Collectors.toList());

Upvotes: 0

Angel Koh
Angel Koh

Reputation: 13505

assume Capital = VISIBLE, small caps = INVISIBLE

AList = { A, a, B, C, b, c, D, d, e }; //both Visible & Invisible.
BList = { C, D, B, A } ; //only Visible with different order.

from your requirement

"So we're replacing every next visible item in A contained in B, with item from B. I wonder if there is more beautiful solution to this problem through either Guava or Stream API."

you want the result to be ->

CList = { C, a, D, B, b, c, A, d, e}; // resort the Visible items to B's order

you can first filter Stream of A to show only visible items, then replace that item ID with the value of B at that relative position. the key is to use AtomicInteger

        AtomicInteger index = new AtomicInteger();
        itemA.stream().filter(a -> a.isVisible)
                .forEach(a -> {
                    a.id = itemB.get(index.getAndIncrement()).id;
                });

The following is the full codes: -

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

public class StreamExample {

    public static void main(String[] args) {
        List<Item> itemA = new ArrayList<>();
        List<Item> itemB = new ArrayList<>();

        itemA.add(new Item("A"));
        itemA.add(new Item("a", false));
        itemA.add(new Item("B"));
        itemA.add(new Item("C"));
        itemA.add(new Item("b", false));
        itemA.add(new Item("c", false));
        itemA.add(new Item("D"));
        itemA.add(new Item("e", false));

        itemB.add(new Item("C"));
        itemB.add(new Item("D"));
        itemB.add(new Item("B"));
        itemB.add(new Item("A"));

        System.out.println("BEFORE: ");
        System.out.println("A: ");
        itemA.forEach(System.out::print);
        System.out.println("\nB: ");
        itemB.forEach(System.out::print);

        AtomicInteger index = new AtomicInteger();
        itemA.stream().filter(a -> a.isVisible)
                .forEach(a -> {
                    a.id = itemB.get(index.getAndIncrement()).id;
                });

        System.out.println("\nAFTER: ");
        System.out.println("A: ");
        itemA.forEach(System.out::print);


    }
}


class Item {
    String id;
    boolean isVisible = true;

    public Item(String id) {
        this.id = id;
    }

    public Item(String id, boolean isVisible) {
        this.id = id;
        this.isVisible = isVisible;
    }

    @Override
    public String toString() {
        return " {'" + id + '\'' +
                '}';
    }
}

The following is the output

BEFORE: 
A: 
 {'A'} {'a'} {'B'} {'C'} {'b'} {'c'} {'D'} {'e'}
B: 
 {'C'} {'D'} {'B'} {'A'}
AFTER: 
A: 
 {'C'} {'a'} {'D'} {'B'} {'b'} {'c'} {'A'} {'e'}

Upvotes: 0

M. le Rutte
M. le Rutte

Reputation: 3563

Implement a comparator that checks if both items are visible, and thus both are in the B list. Use their position in the B list to determine the sort order. If one of them is not visible the order should be on the A list.

class CustomSort implements Comparator<Item> {
    public int compare(Item i1, Item i2) {
        if (i1.isVisible() && i2.isVisible()) {
           return bi.indexOf(i1) - bi.indexOf(i2);
        }

        return ai.indexOf(i1) - ai.indexOf(i2);
    }
}

Upvotes: 2

Related Questions