英文:
Group and sort by multiple attribute using stream: Java 8
问题
我有一个MainEntity的列表
public class MainEntity {
private String keyword;
private double cost;
private String company;
}
我也有CompanyEntity
public class CompanyEntity {
private double cost;
private String company;
}
我试图将我的列表转换为Map<String, List<CompanyEntity>>
,其中键将是keyword
,而List<CompanyEntity>
将具有所有成本的平均值并进行排序。我试图在Java 8中使用流进行此操作。
对于特定的关键词作为输入,我正在做这个。
List<MainEntity> entityList = keyWordMap.get(entity.getKeyword());
entityList.add(entity);
keyWordMap.put(entity.getKeyword(), entityList);
Map<String, Double> average = keyWordMap.get(keyword).stream()
.collect(Collectors.groupingBy(MainEntity::getCompany,
Collectors.averagingDouble(MainEntity::getCost)));
result.put(keyword, new ArrayList<>());
for (Map.Entry<String, Double> entity : average.entrySet()) {
result.get(keyword).add(new CompanyEntity(entity.getKey(), entity.getValue()));
}
但是我想为所有关键词创建一个映射。这是可能的吗?还是再次迭代整个列表有意义?当前keyWordMap
的类型是Map<String, MainEntity>
,我是通过迭代MainEntity
列表来实现的,但我想要Map<String, List<MainEntity>>
。
英文:
I have List of MainEntity
public class MainEntity {
private String keyword;
private double cost;
private String company;
}
and I have CompanyEntity
public class CompanyEntity {
private double cost;
private String company;
}
I am trying to transform my list into Map<String,List<CompanyEntity>>
where key will be keyword
and List<CompanyEntity>
will have average of all the costs and sorted too. I am trying to do it in stream and Java 8.
For a particular keyword as input I am doing this.
List<MainEntity> entityList = keyWordMap.get(entity.getKeyword());
entityList.add(entity);
keyWordMap.put(entity.getKeyword(), entityList);
Map<String, Double> average = (keyWordMap.get(keyword)).stream()
.collect(groupingBy(MainEntity::getCompany,
Collectors.averagingDouble(MainEntity::getCtr)));
result.put(keyword, new ArrayList<>());
for (Map.Entry<String, Double> entity : average.entrySet()) {
result.get(keyword).add(new CompanyEntity(entity.getKey(), entity.getValue()));
}
But I trying to create a map for all keywords. Is is possible or iterating whole list again makes sense?
Currently keyowordMap
is of type Map<String,MainEntity>
which I did by iterating list of MainEntity
, but I want Map<String,List<MainEntity>>
.
答案1
得分: 3
首先,创建一个 keyWordMap
:
Map<String, List<MainEntity>> keyWordMap =
mainEntityList
.stream()
.collect(Collectors.groupingBy(MainEntity::getKeyword));
然后遍历这个映射,对于每个关键词,你可以直接获取按平均值排序的 CompanyEntity
列表,并使用 map()
转换数据并收集为列表,然后放入 result
中:
Map<String, List<CompanyEntity>> result = ....
for (Map.Entry<String, List<MainEntity>> entry : keyWordMap.entrySet()) {
List<CompanyEntity> list = entry.getValue().stream()
.collect(groupingBy(MainEntity::getCompany,
Collectors.averagingDouble(MainEntity::getCtr)))
.entrySet()
.stream()
.sorted(Comparator.comparing(e -> e.getValue()))
.map(e -> new CompanyEntity(e.getKey(), e.getValue()))
.collect(Collectors.toList());
result.put(entry.getKey(), list);
}
或者,如果你想一次性完成这个操作:
Map<String, List<CompanyEntity>> mapData =
mainEntityList
.stream()
.collect(Collectors.groupingBy(MainEntity::getKeyWord,
Collectors.groupingBy(MainEntity::getCtr,
Collectors.averagingDouble(MainEntity::getCtr))))
.entrySet()
.stream()
.collect(Collectors.toMap(m -> m.getKey(),
m -> m.entrySet()
.stream()
.sorted(Comparator.comparing(e -> e.getValue()))
.map(e -> new CompanyEntity(e.getKey(), e.getValue()))
.collect(Collectors.toList())));
英文:
First, make a keyWordMap
Map<String, List<MainEntity>> keyWordMap =
mainEntityList
.stream()
.collect(Collectors.groupingBy(MainEntity::getKeyword));
Then iterate the map, for each keyword, you can directly get the list of CompanyEntity
sort by average value and using map()
to transform the data and collect as List, then put in result
Map<String,List<CompanyEntity>> result = ....
for (Map.Entry<String, List<MainEntity> entry : keyWordMap.entrySet()) {
List<CompanyEntity> list = entry.getValue().stream()
.collect(groupingBy(MainEntity::getCompany,
Collectors.averagingDouble(MainEntity::getCtr)))
.entrySet()
.stream()
.sorted(Comparator.comparing(e -> e.getValue()))
.map(e -> new CompanyEntity(e.getKey(), e.getValue()))
.collect(Collectors.toList());
result.put(entry.getKey(), list);
}
Or you want to do this in one-shot
Map<String,List<CompanyEntity>> mapData =
mainEntityList
.stream()
.collect(Collectors.groupingBy(MainEntity::getKeyWord,
Collectors.groupingBy(MainEntity::getCtr,
Collectors.averagingDouble(MainEntity::getCtr))))
.entrySet()
.stream()
.collect(Collectors.toMap(m -> m.getKey(),
m -> m.entrySet()
.stream()
.sorted(Comparator.comparing(e -> e.getValue()))
.map(e -> new CompanyEntity(e.getKey(), e.getValue()))
.collect(Collectors.toList())));
答案2
得分: 1
以下是翻译好的代码部分:
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Value;
public class CompanyEntityStackOverflowQuestion {
public static void main(String[] args) throws IOException {
// 省略部分,具体内容请查看原文
}
//sort by cost
private static List<CompanyEntity> sortList(List<CompanyEntity> list) {
list.sort(Comparator.comparing(company -> company.cost));
return list;
}
//map MainEntity to CompanyEntity
private static Function<MainEntity, CompanyEntity> getCompanyListFunction() {
return mainEntity -> new CompanyEntity(mainEntity.cost, mainEntity.company);
}
@Value
public static class MainEntity {
// 省略部分,具体内容请查看原文
}
@Value
public static class CompanyEntity {
// 省略部分,具体内容请查看原文
}
@Value
public static class CompanyEntityListWithStats {
// 省略部分,具体内容请查看原文
}
}
输出结果:{key1=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=8.5, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=7.0, company=company3), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=10.0, company=company1)]), key2=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=4.0, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=3.0, company=company4), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=5.0, company=company2)])}
英文:
The other answer completely changed its answer after initially misunderstanding the question and in good StackOverflow spirits it attracted the first upvote so is now accepted and highest upvoted. But this has a few more steps in the code showing what's happening:
This should get you the result:
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Value;
public class CompanyEntityStackOverflowQuestion {
public static void main(String[] args) throws IOException {
//setup test data
MainEntity one = new MainEntity("key1", 10D, "company1");
MainEntity two = new MainEntity("key2", 5D, "company2");
MainEntity three = new MainEntity("key1", 7D, "company3");
MainEntity four = new MainEntity("key2", 3D, "company4");
List<MainEntity> mainEntityList = List.of(one, two, three, four);
//group list by keyword
Map<String, List<MainEntity>> mainEntityByKeyword = mainEntityList.stream()
.collect(Collectors.groupingBy(MainEntity::getKeyword));
//map to companyEntity object
Stream<SimpleEntry<String, List<CompanyEntity>>> mapped = mainEntityByKeyword.entrySet().stream()
.map(entry -> new SimpleEntry<>(entry.getKey(), entry.getValue().stream().map(
getCompanyListFunction()).collect(Collectors.toList())));
//sort and calculate average
Stream<SimpleEntry<String, CompanyEntityListWithStats>> mappedToListWithStats = mapped
.map(entry -> new SimpleEntry<>(entry.getKey(),
new CompanyEntityListWithStats(entry.getValue().stream().mapToDouble(company -> company.cost).average().orElse(0D), //or use Collectors.averagingDouble(company -> company.cost))
sortList(entry.getValue()))));
//collect back to map
Map<String, CompanyEntityListWithStats> collect = mappedToListWithStats
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
//show result
System.out.println(collect);
}
//sort by cost
private static List<CompanyEntity> sortList(List<CompanyEntity> list) {
list.sort(Comparator.comparing(company -> company.cost));
return list;
}
//map MainEntity to CompanyEntity
private static Function<MainEntity, CompanyEntity> getCompanyListFunction() {
return mainEntity -> new CompanyEntity(mainEntity.cost, mainEntity.company);
}
@Value
public static class MainEntity {
public String keyword;
public double cost;
public String company;
}
@Value
public static class CompanyEntity {
public double cost;
public String company;
}
@Value
public static class CompanyEntityListWithStats {
public double average;
public List<CompanyEntity> companyList;
}
}
Output: {key1=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=8.5, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=7.0, company=company3), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=10.0, company=company1)]), key2=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=4.0, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=3.0, company=company4), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=5.0, company=company2)])}
You may be able to skip some steps, this is just quickly typed out. You can of course inline stuff to make it look a lot shorter/cleaner, but this format shows what's happening.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论