Drew13
Drew13

Reputation: 1371

Find matches in one list and remove them

I have one list in which I need to loop through the list and find matches in order to combine them and add them to another list. The problem is I keep having issues with iterating over some elements multiple times or skipping over certain elements.

The objects in the list each have two int values. If the first int value is a match between two elements, I need to combine both object's second int value and add that object to another list

If I have a list ((12,10), (13,10), (12,5), (14,5), (14,10), (10,20)) I would like to combine the 12's and 14's into a new list to make ((12,15), (14,15)) and my original list would be left with ((13,10), (10,20))

I've tried using a for loop with and without an Iterator, but with no luck.

List<Record> finalList = new ArrayList<Record>();

for (int i = 0; i < tempAggregateList.size(); i++){
            Record record = tempAggregateList.get(i);
            for (int j = i+1; j < tempAggregateList.size(); j++){
                Record nextRecord = tempAggregateList.get(j);
                if (record.getFirstValue() == nextRecord.getFirstValue()){
                    record.setSecondValue(record.getSecondValue() + nextRecord.getSecondValue());
                    //then remove nextRecord from tempAggregateList
                }
            }
            finalList.add(record);
        }

The way I have it now, my first for loop will loop over elements that I've already combined and added to finalList. It'd be easiest to just remove elements that I've added to finalList , but I haven't found a way yet.

Upvotes: 1

Views: 611

Answers (4)

daniu
daniu

Reputation: 14999

You should be able to use streams:

// group by first value
Map<Integer, Record> newList = list.stream().collect(Collectors.groupingBy(Record::getFirstValue,
       // for each record list for the same first value, add the second one          
       Collectors.collectingAndThen(
            // add first and second value for each pair
            Collectors.reducing((r1, r2) -> new Record(r1.getFirstValue(), r1.getSecondValue() + r2.getSecondValue())),
            // there will be no empty list, so all optionals will be present
            Optional::get));

Now you have a Map<Integer, Record> with the keys being the first value and the key being the combined Record.

EDIT: I realized this could be done somewhat easier by

BinaryOperator<Record> addRecords = (r1, r2) -> new Record(r1.getFirstValue(), r1.getSecondValue() + r2.getSecondValue());

Map<Integer, Record> map = list.stream().collect(
    toMap(Record::getFirstValue, r -> r, addRecords));

and if you want a list and do this in one line

List<Record> result = list.stream().collect(
    collectingAndThen(
        toMap(Record::getFirstValue, r -> r, addRecords),
        m -> new ArrayList<>(m.values())));

Static import from Collectors are implied.

Upvotes: 3

SedJ601
SedJ601

Reputation: 13859

This uses groupingBy to get a List of List. if the List has a length greater than one it is added to the final List. if the Listhas a length equal to 1, it is kept in the original List.

List<Record> originalList = new ArrayList();
originalList.add(new Record(12, 10));
originalList.add(new Record(13, 10));
originalList.add(new Record(12, 5));
originalList.add(new Record(14, 5));
originalList.add(new Record(14, 10));
originalList.add(new Record(10, 20));

List<Record> finalList = originalList.stream().collect(groupingBy(Record::getFirstValue)).values().stream().filter((t) -> {
    return t.size() > 1; 
}).flatMap(x -> x.stream()).collect(Collectors.toList()).stream().collect(groupingBy(Record::getFirstValue, Collectors.summingInt(Record::getSecondValue))).entrySet().stream().map(t -> new Record(t.getKey(), t.getValue())).collect(Collectors.toList());
finalList.forEach((t) -> {
    System.out.println("(" + t.getFirstValue() + ", " + t.getSecondValue() + ")");
});

originalList = originalList.stream().collect(groupingBy(Record::getFirstValue)).values().stream().filter((t) -> {
    return t.size() == 1;
}).flatMap(x -> x.stream()).collect(Collectors.toList()).stream().collect(groupingBy(Record::getFirstValue, Collectors.summingInt(Record::getSecondValue))).entrySet().stream().map(t -> new Record(t.getKey(), t.getValue())).collect(Collectors.toList());

originalList.forEach((t) -> {
    System.out.println("(" + t.getFirstValue() + ", " + t.getSecondValue() + ")");
});

output

(12, 15)
(14, 15)
(10, 20)
(13, 10)

Upvotes: 0

forpas
forpas

Reputation: 164089

Just for the fun of it, iterations, a Set to store the indices to be removed, and reverse iteration for removing the items from the initial list:

public static void main(String[] args) {
    List<Record> tempAggregateList = new ArrayList<Record>();
    tempAggregateList.add(new Record(12, 10));
    tempAggregateList.add(new Record(13, 10));
    tempAggregateList.add(new Record(12, 5));
    tempAggregateList.add(new Record(14, 5));
    tempAggregateList.add(new Record(14, 10));
    tempAggregateList.add(new Record(10, 20));

    List<Record> finalList = new ArrayList<>();
    Set<Integer> set = new HashSet<>();

    for (int i = 0; i < tempAggregateList.size(); i++) {
        if (!set.contains(i)) {
            Record record = tempAggregateList.get(i);
            int baseValue = record.getFirstValue();
            int sum = record.getSecondValue();
            boolean found = false;

            for (int j = i + 1; j < tempAggregateList.size(); j++) {
                Record nextRecord = tempAggregateList.get(j);
                if (nextRecord.getFirstValue() == baseValue) {
                    found = true;
                    set.add(i);
                    set.add(j);
                    sum += nextRecord.getSecondValue();
                }
            }
            if (found)
                finalList.add(new Record(baseValue, sum));
        }
    }

    for (int i = tempAggregateList.size() - 1; i >= 0; i--) {
        if (set.contains(i))
            tempAggregateList.remove(i);
    }

    for (Record r : tempAggregateList) {
        System.out.print("(" + r.getFirstValue() + ", " + r.getSecondValue() + ") ");
    }

    System.out.println();

    for (Record r : finalList) {
        System.out.print("(" + r.getFirstValue() + ", " + r.getSecondValue() + ") ");
    }
}

will print:

(13, 10) (10, 20) 
(12, 15) (14, 15) 

Upvotes: 0

Invisible
Invisible

Reputation: 112

After finalList.add(record), simple add tempAggregateList.remove(i);

Upvotes: 1

Related Questions