图表数据转化
目标:
现在前端所需的图标数据格式大致统一,后端从数据库查询后的数据种类多种多样,希望可以通过常见的转化方法转为工具类,提高业务开发效率。
常见的数据表格式说明
下面是常见前端框架(Vue、React)中使用的图表数据格式的总结:
Vue
Vue Chart.js
-
Line Chart:
- 数据格式:
labels
:标签数组,表示 x 轴上的数据点。- datasets:数据集数组,每个数据集包含以下属性:
label
:数据集的标签。data
:数据点数组,表示 y 轴上的数据。borderColor
:线条颜色。fill
:是否填充面积。
- 示例代码:
data() { return { chartData: { labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], datasets: [ { label: 'Data 1', data: [65, 59, 80, 81, 56, 55, 40], borderColor: 'red', fill: false }, { label: 'Data 2', data: [28, 48, 40, 19, 86, 27, 90], borderColor: 'blue', fill: false } ] } } }
- 数据格式:
-
Bar Chart:
- 数据格式:
labels
:标签数组,表示 x 轴上的数据点。- datasets:数据集数组,每个数据集包含以下属性:
label
:数据集的标签。data
:数据点数组,表示 y 轴上的数据。backgroundColor
:柱状图的填充颜色。
- 示例代码:
data() { return { chartData: { labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], datasets: [ { label: 'Data 1', data: [12, 19, 3, 5, 2, 3], backgroundColor: 'red' }, { label: 'Data 2', data: [15, 9, 7, 8, 5, 7], backgroundColor: 'blue' } ] } } }
- 数据格式:
Vue ECharts
-
Line Chart:
- 数据格式:
- xAxis:x 轴配置,包含以下属性:
type
:x 轴类型(例如 category)。data
:标签数组,表示 x 轴上的数据点。
- yAxis:y 轴配置,包含以下属性:
type
:y 轴类型。
- series:系列数组,每个系列包含以下属性:
name
:系列的名称。type
:图表类型(例如 line)。data
:数据点数组,表示 y 轴上的数据。
- xAxis:x 轴配置,包含以下属性:
- 示例代码:
data() { return { chartData: { xAxis: { type: 'category', data: ['January', 'February', 'March', 'April', 'May', 'June', 'July'] }, yAxis: { type: 'value' }, series: [ { name: 'Data 1', type: 'line', data: [65, 59, 80, 81, 56, 55, 40] }, { name: 'Data 2', type: 'line', data: [28, 48, 40, 19, 86, 27, 90] } ] } } }
- 数据格式:
-
Bar Chart:
- 数据格式:
- xAxis:x 轴配置,包含以下属性:
type
:x 轴类型(例如 category)。data
:标签数组,表示 x 轴上的数据点。
- yAxis:y 轴配置,包含以下属性:
type
:y 轴类型。
- series:系列数组,每个系列包含以下属性:
name
:系列的名称。type
:图表类型(例如 bar)。data
:数据点数组,表示 y 轴上的数据。
- xAxis:x 轴配置,包含以下属性:
- 示例代码:
data() { return { chartData: { xAxis: { type: 'category', data: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'] }, yAxis: { type: 'value' }, series: [ { name: 'Data 1', type: 'bar', data: [12, 19, 3, 5, 2, 3] }, { name: 'Data 2', type: 'bar', data: [15, 9, 7, 8, 5, 7] } ] } } }
- 数据格式:
React
React Chart.js
- Line Chart:
- 数据格式与 Vue Chart.js 中的相同。
- Bar Chart:
- 数据格式与 Vue Chart.js 中的相同。
React ECharts
- Line Chart:
- 数据格式与 Vue ECharts 中的相同。
- Bar Chart:
- 数据格式与 Vue ECharts 中的相同。
后端统一返回数据格式
Json展示
{
"code": 200,
"message": "成功",
"data": {
"x": [
"January",
"February",
"March"
],
"y": [
{
"name": "Data 1",
"data": [
"77.34",
"72.65",
"74.18"
]
},
{
"name": "Data 2",
"data": [
"70.67",
"79.54",
"74.75"
]
},
{
"name": "Data 3",
"data": [
"74.66",
"75.39",
"74.12"
]
}
]
}
}
实体类封装:
x轴,y轴分组:
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
public class TableListVo {
@ApiModelProperty(value = "x轴数据")
private List<String> x;
@ApiModelProperty(value = "y轴数据")
private List<TableYListVo> y;
public TableListVo(List<String> x, List<TableYListVo> y) {
this.x = x;
this.y = y;
}
}
y轴数据:
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
public class TableYListVo {
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "数据")
private List<String> data;
public TableYListVo(String name, List<String> data) {
this.name = name;
this.data = data;
}
}
抽取常见的转化方式
1.根据查询的数据作为分组展示依据
数据库查询出的数据:
<== Columns: average, variety, city
<== Row: 70.67, apple, New York
<== Row: 74.75, apple, Los Angeles"
<== Row: 77.34, banana, New York
<== Row: 74.18, banana, Los Angeles"
<== Row: 72.65, banana, Chicago
<== Row: 74.12, orange, Los Angeles"
<== Row: 75.39, orange, Chicago
<== Row: 79.54, apple, Chicago
<== Row: 74.66, orange, New York
需求:现在希望将city作为x轴数据,variety作为y轴的name数据,average为y轴的data属性。达到的效果为,每个城市对应的品种数据以表格的形式展出,若average则赋值为0
效果图:
转化工具类:
/**
* 将数据列表转换为表格数据,可以排除指定的 x 字段和 y 字段
*
* @param dataList 数据列表
* @param xFieldExtractor x 字段值提取函数
* @param yFieldExtractor y 字段值提取函数
* @param valueFieldExtractor 值字段值提取函数
* @param excludedXFields 要排除的 x 字段值列表
* @param excludedYFields 要排除的 y 字段值列表
* @param fieldLabelProvider 字段标签提供函数
* @param <T> 数据类型
* @return 表格数据对象
*/
public static <T> TableListVo convertToTableDataWithExclusions(List<T> dataList, Function<T, String> xFieldExtractor, Function<T, String> yFieldExtractor, Function<T, String> valueFieldExtractor, List<String> excludedXFields, List<String> excludedYFields, Function<String, String> fieldLabelProvider) {
try {
// 创建一个数据映射的嵌套数据结构,用于存储每个字段的数据
Map<String, Map<String, List<String>>> dataMap = dataList.stream().filter(obj -> excludedXFields == null || !excludedXFields.contains(xFieldExtractor.apply(obj))).collect(Collectors.groupingBy(obj -> xFieldExtractor.apply(obj), Collectors.groupingBy(obj -> yFieldExtractor.apply(obj), Collectors.mapping(obj -> String.valueOf(valueFieldExtractor.apply(obj)), Collectors.toList()))));
// 提取 x 轴的标签,即 xField 的值列表
List<String> xLabels = new ArrayList<>(dataMap.keySet());
// 获取所有可能的 y 字段值
List<String> allYValues = dataMap.values().stream().flatMap(yData -> yData.keySet().stream()).filter(yValue -> excludedYFields == null || !excludedYFields.contains(yValue)).distinct().collect(Collectors.toList());
// 构建 y 轴的数据列表
List<TableYListVo> yData = allYValues.stream().map(yValue -> {
String mappedYValue = fieldLabelProvider != null ? fieldLabelProvider.apply(yValue) : yValue;
List<String> yValueData = xLabels.stream().map(xLabel -> dataMap.getOrDefault(xLabel, new HashMap<>()).getOrDefault(yValue, new ArrayList<>())).flatMap(List::stream).collect(Collectors.toList());
return new TableYListVo(mappedYValue, yValueData);
}).collect(Collectors.toList());
return new TableListVo(xLabels, yData);
} catch (Exception e) {
// 异常处理,记录日志等
e.printStackTrace();
return new TableListVo(new ArrayList<>(), new ArrayList<>());
}
}
调用:
List<TobaccoSensoryDto> resultList = tobaccoInfoMapper.selectBySensory();
TableListVo tableListVo1 = TableUtils.convertToTableDataWithExclusions(resultList,TobaccoSensoryDto::getCity , TobaccoSensoryDto::getVariety, TobaccoSensoryDto::getAverage,
null,CollectionUtil.newArrayList("红大","政和红大"),null);
2.根据查询的字段作为分组展示依据
数据库查询出的数据:
<== Columns: aroma_quality, aroma_amount, impurity, mellowness, concentration, strength, irritation, aftertaste, create_year
<== Row: 6.95, 7.18, 6.77, 6.86, 7.59, 7.23, 6.82, 6.77, 2013
<== Row: 6.88, 7.42, 6.85, 6.88, 7.73, 6.77, 6.62, 6.73, 2014
<== Row: 7.42, 7.65, 7.27, 7.27, 7.81, 7.35, 7.27, 7.23, 2015
<== Row: 7.5, 7.5, 7.38, 7.46, 7.85, 7.69, 7.27, 7.38, 2016
<== Row: 7.81, 7.92, 7.5, 7.88, 8.04, 7.81, 7.69, 7.69, 2017
<== Row: 7.5, 7.8, 7.33, 7.3, 8.23, 7.77, 7.2, 7.27, 2018
<== Row: 7.96, 8.11, 7.79, 7.75, 8.09, 7.96, 7.75, 7.71, 2019
<== Row: 7.54, 7.58, 7.08, 7.33, 7.42, 7.33, 7.33, 7.08, 2020
<== Row: 7.25, 7.83, 7.08, 7.21, 8.04, 7.38, 7.25, 7.17, 2021
需求:现在希望将create_year作为x轴数据,查询字段作为作为y轴的name数据,查询的字段值为y轴的data属性。达到的效果为,每年对应的9字段数据以表格的形式展出,若average则赋值为0
效果图:
转化工具类:
/**
* 将数据列表转换为表格数据
*
* @param dataList 数据列表
* @param xFieldExtractor 提取 x 字段值的函数
* @param yFields y 字段的列表
* @param fieldLabelProvider 获取字段标签值的函数
* @param <T> 数据类型
* @return 表格数据对象
*/
public static <T> TableListVo convertToTableData(List<T> dataList, Function<T, String> xFieldExtractor, List<String> yFields, Function<String, String> fieldLabelProvider) {
try {
// 创建一个数据映射的嵌套数据结构,用于存储每个字段的数据
Map<String, Map<String, List<String>>> dataMap = new HashMap<>();
// 初始化数据映射结构,为每个字段创建一个子映射
yFields.forEach(yField -> dataMap.put(yField, new HashMap<>()));
// 遍历数据列表,将数据按字段存储到数据映射中
dataList.forEach(data -> {
String xValue = xFieldExtractor.apply(data);
yFields.forEach(yField -> {
String yValue = getField(data, yField);
// 将数据根据 x 值和对应的字段存储到数据映射中
if (yValue == null) {
yValue = "0"; // 将 null 值替换为 "0"
}
dataMap.get(yField).computeIfAbsent(xValue, k -> new ArrayList<>()).add(yValue);
});
});
// 提取 x 轴的标签,即 xFieldExtractor 获得的值列表
List<String> xLabels = new ArrayList<>(dataMap.get(yFields.get(0)).keySet());
// 判断 x 字段的数据类型,并根据需要进行排序
if (isNumeric(xLabels.get(0))) {
xLabels.sort(Comparator.comparingDouble(Double::parseDouble));
}
// 构建 y 轴的数据列表
List<TableYListVo> yData = yFields.stream().map(yField -> {
String fieldLabel = fieldLabelProvider.apply(yField);
// 提取每个字段对应的数据值
List<String> yValueData = dataMap.get(yField).values().stream().flatMap(List::stream).collect(Collectors.toList());
return new TableYListVo(fieldLabel, yValueData);
}).collect(Collectors.toList());
return new TableListVo(xLabels, yData);
} catch (Exception e) {
// 异常处理,记录日志等
e.printStackTrace();
return new TableListVo(new ArrayList<>(), new ArrayList<>());
}
}
// 判断字符串是否为数字
private static boolean isNumeric(String str) {
if (str == null) {
return false;
}
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* 获取对象的字段值
*
* @param data 数据对象
* @param fieldName 字段名
* @param <T> 数据类型
* @return 字段值
*/
private static <T> String getField(T data, String fieldName) {
if (data == null || fieldName == null || fieldName.isEmpty()) {
return null;
}
Class<?> clazz = data.getClass();
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(data).toString();
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
调用:
List<TobaccoSensoryEval> resultList = tobaccoChemicalEvalMapper.getHistorySensory(gradekey.getGrade());
TableListVo tableListVo = TableUtils.convertToTableData(resultList, TobaccoSensoryEval::getCreateYear, CollectionUtil.newArrayList("aromaQuality", "aromaAmount"), TobaccoSensoryEvalField::getByFieldName);
System.out.println("tableListVo = " + tableListVo);
标签:转化,return,data,List,param,图表,new,数据
From: https://www.cnblogs.com/RookieMaster/p/17851672.html