Combine Multiple Collections into a Single Logical Collection

Combine multiple Collections into a single logical Collection?

With Guava, you can use Iterables.concat(Iterable<T> ...), it creates a live view of all the iterables, concatenated into one (if you change the iterables, the concatenated version also changes). Then wrap the concatenated iterable with Iterables.unmodifiableIterable(Iterable<T>) (I hadn't seen the read-only requirement earlier).

From the Iterables.concat( .. ) JavaDocs:

Combines multiple iterables into a
single iterable. The returned iterable
has an iterator that traverses the
elements of each iterable in inputs.
The input iterators are not polled
until necessary. The returned
iterable's iterator supports remove()
when the corresponding input iterator
supports it.

While this doesn't explicitly say that this is a live view, the last sentence implies that it is (supporting the Iterator.remove() method only if the backing iterator supports it is not possible unless using a live view)

Sample Code:

final List<Integer> first  = Lists.newArrayList(1, 2, 3);
final List<Integer> second = Lists.newArrayList(4, 5, 6);
final List<Integer> third = Lists.newArrayList(7, 8, 9);
final Iterable<Integer> all =
Iterables.unmodifiableIterable(
Iterables.concat(first, second, third));
System.out.println(all);
third.add(9999999);
System.out.println(all);

Output:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 9999999]


Edit:

By Request from Damian, here's a similar method that returns a live Collection View

public final class CollectionsX {

static class JoinedCollectionView<E> implements Collection<E> {

private final Collection<? extends E>[] items;

public JoinedCollectionView(final Collection<? extends E>[] items) {
this.items = items;
}

@Override
public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}

@Override
public void clear() {
for (final Collection<? extends E> coll : items) {
coll.clear();
}
}

@Override
public boolean contains(final Object o) {
throw new UnsupportedOperationException();
}

@Override
public boolean containsAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}

@Override
public boolean isEmpty() {
return !iterator().hasNext();
}

@Override
public Iterator<E> iterator() {
return Iterables.concat(items).iterator();
}

@Override
public boolean remove(final Object o) {
throw new UnsupportedOperationException();
}

@Override
public boolean removeAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}

@Override
public boolean retainAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}

@Override
public int size() {
int ct = 0;
for (final Collection<? extends E> coll : items) {
ct += coll.size();
}
return ct;
}

@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}

@Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException();
}

@Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}

}

/**
* Returns a live aggregated collection view of the collections passed in.
* <p>
* All methods except {@link Collection#size()}, {@link Collection#clear()},
* {@link Collection#isEmpty()} and {@link Iterable#iterator()}
* throw {@link UnsupportedOperationException} in the returned Collection.
* <p>
* None of the above methods is thread safe (nor would there be an easy way
* of making them).
*/
public static <T> Collection<T> combine(
final Collection<? extends T>... items) {
return new JoinedCollectionView<T>(items);
}

private CollectionsX() {
}

}

Merge several CollectionsA into a single CollectionA from a CollectionB using Guava

You can use a function to extract the list and concat to flatten the lists. This results in an Iterable.

List<A> input;
Function<A, List<B>> t = new Function<A, List<B>>() {
@Override public List<B> apply(A input) {
return input.myListOfB;
}
};
Iterable<B> transform = Iterables.concat(Iterables.transform(input, t));

You can create an List if you need it:

ImmutableList<B> asList = ImmutableList.copyOf(transform);
//or
List<B> newArrayList = Lists.newArrayList(transform);

Note: Normally, public fields of classes are static and immutable or private. Everything else will bring you in trouble.

How to flatten the structure of multiple collections into a single one with a common id with streams

If I understand correctly, you want to do:

public List<ListDetails> groupListWithProductsById1(List<ListWithDetailsRow> listWithDetailsRows) {
return listWithDetailsRows.stream()
.collect(Collectors.groupingBy(ListWithDetailsRow::getListId)).values().stream()
.map(rows ->
new ListDetails(
rows.get(0).getName(),
rows.get(0).getListId(),
rows.get(0).getCustomerId(),
rows.get(0).getCreatedAt(),
rows.get(0).getUpdatedAt(),
rows.stream().map(row -> new ListProductDetails(
row.getProductId(), row.getQuantity(), row.getAddedAt()
)).collect(Collectors.toList())
)
).collect(Collectors.toList());
}

After grouping by the list ID, you have a Collection<List<ListWithDetailsRow>> as the values() of the returned map. Each of the lists in the collection represents a group.

You can then map each group to a ListDetails. The name, listId, customerId, createdAt and updatedAt fields of the ListDetails can be taken from an arbitrary element in the group (I've chosen the first one), since every element should have the same values for those fields.

For the products field, just map the ListWithDetailsRows to ListProductDetails and collect to a list.

Create a single collection from multiple collections in a way that maximises distance between elements of the same initial collection

You can flatten values using zipWithIndex to combine them with index inside every group then group by that index and flatten again:

grouped.values
.flatMap(xs => xs.zipWithIndex)
.groupBy(_._2)
.values
.flatMap(_.unzip._1)

How do I merge two different collections of same database into a new collection having similar field values in MongoDB?

the real problem was type casting. I failed to notice that unitId was a number(int32) but Unitid was a string, because of this aggregation operation was not carried out. Mongodb community forum helped me to realize such a trivial mistake. Here is the link to that. The code with type conversion is as follows:

    [
{
'$addFields': {
'unitId': {
'$toString': '$unitId'
}
}
}, {
'$lookup': {
'from': 'datas',
'localField': 'unitId',
'foreignField': 'elevatorInfo.Institution_Characteristics.Unitid',
'as': 'nice'
}
}, {
'$unwind': {
'path': '$nice'
}
}, {
'$project': {
'__v': 0,
'_id': 0
}
}
]

Mapstruct - mapping from multiple collections into a single collection

So I would make a mapping from my SourceObject (not write List<UserNumber> listsToDtoList(List<PhoneNumber> phoneNumbers, List<FaxNumber> faxNumbers);

So something along the lines:

@Mapping( target = "userNumbers", source = "phoneNumbers" )
Target map( Source source );

@AfterMapping // will be applied in the final part of the previous method
default void map ( Source source, @MappingTarget Target target ) {
target.getUserNumbers().addAll(source.getFaxNumbers());
}


Related Topics



Leave a reply



Submit