mirror of https://github.com/alibaba/easyexcel
2 changed files with 223 additions and 0 deletions
@ -0,0 +1,132 @@ |
|||||||
|
package com.alibaba.excel.util; |
||||||
|
import org.apache.commons.compress.utils.Lists; |
||||||
|
import org.apache.commons.math3.util.Pair; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author phaeris |
||||||
|
* @since 2023/12/15 |
||||||
|
*/ |
||||||
|
public class PivotUtils { |
||||||
|
|
||||||
|
private PivotUtils() {} |
||||||
|
|
||||||
|
/** |
||||||
|
* get pivot table head and data |
||||||
|
* |
||||||
|
* @param rowFields pivot row fields |
||||||
|
* @param columnFields pivot column fields |
||||||
|
* @param aggFields aggregator fields |
||||||
|
* @param data original data |
||||||
|
* @return head & data |
||||||
|
*/ |
||||||
|
public static Pair<List<List<String>>, List<List<Object>>> getHeadAndData(List<String> rowFields, List<String> columnFields, List<String> aggFields, |
||||||
|
List<Map<String, Object>> data) { |
||||||
|
return getHeadAndData(rowFields, columnFields, aggFields, null, data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get pivot table head and data |
||||||
|
* |
||||||
|
* @param rowFields pivot row fields |
||||||
|
* @param columnFields pivot column fields |
||||||
|
* @param aggFields aggregator fields |
||||||
|
* @param alias alias mapping |
||||||
|
* @param data original data |
||||||
|
* @return head & data |
||||||
|
*/ |
||||||
|
public static Pair<List<List<String>>, List<List<Object>>> getHeadAndData(List<String> rowFields, List<String> columnFields, List<String> aggFields, |
||||||
|
Map<String, String> alias, List<Map<String, Object>> data) { |
||||||
|
//current row size empty data
|
||||||
|
final List<Object> emptyRowData = rowFields.stream().map(x -> null).collect(Collectors.toList()); |
||||||
|
//current column size empty data
|
||||||
|
final List<Object> emptyColData = columnFields.stream().map(x -> null).collect(Collectors.toList()); |
||||||
|
|
||||||
|
//left top head
|
||||||
|
List<List<String>> centerHeadPart = rowFields.stream().map(col -> { |
||||||
|
List<String> centerHead = new ArrayList<>(columnFields); |
||||||
|
centerHead.add(col); |
||||||
|
return centerHead; |
||||||
|
}).collect(Collectors.toList()); |
||||||
|
//aggregator head
|
||||||
|
List<List<String>> aggHeadPart = Lists.newArrayList(); |
||||||
|
|
||||||
|
//data init, fill row data
|
||||||
|
Map<List<Object>, List<Object>> dataList = data.stream() |
||||||
|
.map(row -> getGroup(row, rowFields, emptyRowData)) |
||||||
|
.distinct() |
||||||
|
.collect(Collectors.toMap(x -> x, ArrayList::new, (o, n) -> n, LinkedHashMap::new)); |
||||||
|
//original data group by columns
|
||||||
|
Map<List<Object>, List<Map<String, Object>>> groupByCols = data.stream() |
||||||
|
.collect(Collectors.groupingBy(row -> getGroup(row, columnFields, emptyColData), LinkedHashMap::new, Collectors.toList())); |
||||||
|
|
||||||
|
//fill aggregator head and data
|
||||||
|
for (Map.Entry<List<Object>, List<Map<String, Object>>> eachColData : groupByCols.entrySet()) { |
||||||
|
//each column head
|
||||||
|
List<String> colHead = eachColData.getKey().stream() |
||||||
|
.map(x -> Objects.isNull(x) ? StringUtils.EMPTY : String.valueOf(x)) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
//each col data group by row
|
||||||
|
Map<List<Object>, List<Map<String, Object>>> groupByRows = eachColData.getValue() |
||||||
|
.stream() |
||||||
|
.collect(Collectors.groupingBy(x -> getGroup(x, rowFields, emptyRowData))); |
||||||
|
//aggregator head nums: column data size * aggregator nums
|
||||||
|
for (String aggField : aggFields) { |
||||||
|
//col head
|
||||||
|
List<String> copyColHead = new ArrayList<>(colHead); |
||||||
|
copyColHead.add(aggField); |
||||||
|
aggHeadPart.add(copyColHead); |
||||||
|
//this col all data for each row
|
||||||
|
dataList.forEach((originalRowData, finalRowData) -> { |
||||||
|
List<Map<String, Object>> curRowData = groupByRows.get(originalRowData); |
||||||
|
if (curRowData != null && !curRowData.isEmpty()) { |
||||||
|
//group by column and row, get first
|
||||||
|
finalRowData.add(curRowData.get(0).get(aggField)); |
||||||
|
} else { |
||||||
|
//no match, fill empty
|
||||||
|
finalRowData.add(null); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//merge and replace alias head
|
||||||
|
centerHeadPart.addAll(aggHeadPart); |
||||||
|
List<List<String>> head = centerHeadPart.stream() |
||||||
|
.map(x -> x.stream().map(c -> getAlias(alias, c)).collect(Collectors.toList())) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
return Pair.create(head, new ArrayList<>(dataList.values())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get group |
||||||
|
* |
||||||
|
* @param row row data |
||||||
|
* @param fields fields |
||||||
|
* @param emptyData or else empty data object |
||||||
|
* @return group |
||||||
|
*/ |
||||||
|
private static List<Object> getGroup(Map<String, Object> row, List<String> fields, List<Object> emptyData) { |
||||||
|
List<Object> dataList = fields.stream() |
||||||
|
.map(row::get) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
List<Object> nonNull = dataList.stream().filter(Objects::nonNull).collect(Collectors.toList()); |
||||||
|
return nonNull.isEmpty() ? emptyData : dataList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get head alias |
||||||
|
* |
||||||
|
* @param alias alias mapping |
||||||
|
* @param field field |
||||||
|
* @return alias of field |
||||||
|
*/ |
||||||
|
private static String getAlias(Map<String, String> alias, String field) { |
||||||
|
return alias != null && !alias.isEmpty() && StringUtils.isNotBlank(alias.get(field)) |
||||||
|
? alias.get(field) |
||||||
|
: field; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package com.alibaba.easyexcel.test.core.pivot; |
||||||
|
|
||||||
|
import com.alibaba.excel.EasyExcelFactory; |
||||||
|
import com.alibaba.excel.ExcelWriter; |
||||||
|
import com.alibaba.excel.util.ListUtils; |
||||||
|
import com.alibaba.excel.util.PivotUtils; |
||||||
|
import com.alibaba.excel.write.metadata.WriteSheet; |
||||||
|
import org.apache.commons.math3.util.Pair; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author phaeris |
||||||
|
* @since 2023/12/15 |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public class PivotTest { |
||||||
|
|
||||||
|
private static final String EXPORT_FILE_PATH = "C:\\Users\\A11-9\\Desktop\\excel\\pivot.xlsx"; |
||||||
|
|
||||||
|
private static final String SHEET_NAME = "pivot"; |
||||||
|
|
||||||
|
public static final List<String> ROW_FIELDS = ListUtils.newArrayList("address"); |
||||||
|
|
||||||
|
public static final List<String> COLUMN_FIELDS = ListUtils.newArrayList("name", "phone"); |
||||||
|
|
||||||
|
public static final List<String> AGG_FIELDS = ListUtils.newArrayList("age", "height"); |
||||||
|
|
||||||
|
private static final Map<String, String> ALIAS; |
||||||
|
|
||||||
|
private static final List<Map<String, Object>> DATA; |
||||||
|
|
||||||
|
static { |
||||||
|
Map<String, Object> row1 = new HashMap<>(); |
||||||
|
row1.put("address", "上海"); |
||||||
|
row1.put("name", "张三"); |
||||||
|
row1.put("phone", "110"); |
||||||
|
row1.put("age", "18"); |
||||||
|
row1.put("height", "170"); |
||||||
|
Map<String, Object> row2 = new HashMap<>(); |
||||||
|
row2.put("address", "上海"); |
||||||
|
row2.put("name", "李四"); |
||||||
|
row2.put("phone", "120"); |
||||||
|
row2.put("age", "19"); |
||||||
|
row2.put("height", "173"); |
||||||
|
Map<String, Object> row3 = new HashMap<>(); |
||||||
|
row3.put("address", "北京"); |
||||||
|
row3.put("name", "王五"); |
||||||
|
row3.put("phone", "119"); |
||||||
|
row3.put("age", "22"); |
||||||
|
row3.put("height", "175"); |
||||||
|
Map<String, Object> row4 = new HashMap<>(); |
||||||
|
row4.put("address", "北京"); |
||||||
|
row4.put("name", "赵六"); |
||||||
|
row4.put("phone", "114"); |
||||||
|
row4.put("age", "30"); |
||||||
|
row4.put("height", "180"); |
||||||
|
DATA = ListUtils.newArrayList(row1, row2, row3, row4); |
||||||
|
Map<String, String> alias = new HashMap<>(); |
||||||
|
alias.put("name", "姓名"); |
||||||
|
alias.put("phone", "手机号"); |
||||||
|
alias.put("address", "地址"); |
||||||
|
alias.put("age", "年龄"); |
||||||
|
alias.put("height", "身高"); |
||||||
|
ALIAS = alias; |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void testExportPivot() { |
||||||
|
Pair<List<List<String>>, List<List<Object>>> headAndData = PivotUtils.getHeadAndData(ROW_FIELDS, COLUMN_FIELDS, AGG_FIELDS, DATA); |
||||||
|
write(headAndData); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void testExportPivotHaveAlias() { |
||||||
|
Pair<List<List<String>>, List<List<Object>>> headAndData = PivotUtils.getHeadAndData(ROW_FIELDS, COLUMN_FIELDS, AGG_FIELDS, ALIAS, DATA); |
||||||
|
write(headAndData); |
||||||
|
} |
||||||
|
|
||||||
|
private void write(Pair<List<List<String>>, List<List<Object>>> headAndData) { |
||||||
|
ExcelWriter writer = EasyExcelFactory.write(EXPORT_FILE_PATH).build(); |
||||||
|
WriteSheet sheet = EasyExcelFactory.writerSheet(SHEET_NAME) |
||||||
|
.head(headAndData.getKey()) |
||||||
|
.build(); |
||||||
|
writer.write(headAndData.getValue(), sheet); |
||||||
|
writer.finish(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue