Reputation: 1371
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
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
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 List
has 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
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
Reputation: 112
After finalList.add(record), simple add tempAggregateList.remove(i);
Upvotes: 1