现有基本表(Base table)以及从基本表对其不同维度(dimensions)值组合(其中*表示所有ALL)进行聚集计算(下图为AVG,取平均)得到的聚集表(Measure),例子如下:
上图右边的聚集表可展开如下图表示:
请选择一种擅长的语言设计和实现相关算法,从基本表计算相应的聚集表。
这里是javascript的算法实现:
// 基本数据 修改基本数据要修改table结构
let baseArr = [
['S1', 'P1', 'Spring', 6],
['S1', 'P2', 'Spring', 12],
['S2', 'P1', 'Fall', 9],
]
// 数据有效长度,因为sales不计入聚合
const effeLength = baseArr[0].length - 1;
// 聚合核心算法
function aggregate(aggregateArr, baseArr, aggregateVal) {
let obj = {}; // 使用hash来对相同数据去重(由于对象具有hash的作用,这里直接使用对象就不用map了
for (let i = 0; i < effeLength; i++) { // 遍历列 01 12 20
let copyArr = JSON.parse(JSON.stringify(baseArr)) // 深拷贝,防止后续处理污染原始数据
copyArr.forEach(v => {
let index = i; // 记录当前聚合列的索引
for (let j = 0; j < aggregateVal; j++) { // aggregateVal 的值代表聚合的列数
v[index] = '*'
index++;
if (index >= effeLength) {
index = 0 // 形成索引环 超过数据长度就从头开始
}
}
// 将将列合并,方便数据去重,合并相同项,并计算sales和与出现次数(用以计算平均值)
let key = '';
for (let j = 0; j < effeLength; j++) {
key += v[j] + ','
}
if (obj[key]) { // 数据如果已存在,则累加sales值,并记录出现次数
obj[key].value += v[effeLength]
obj[key].frequency += 1
} else { // 没有就新建一个
obj[key] = {} // obj= {key:{}} <==> obj.key = {}
obj[key].value = v[effeLength] // obj.key = {value: sales}
obj[key].frequency = 1 // obj.key = {value:sales,frequency:1}
}
});
}
for (let key in obj) {
let temp = key.split(','); // 分割出去重后的列
temp[effeLength] = obj[key].value / obj[key].frequency // 重新计算sales平均值
aggregateArr.push(temp) // 加入待渲染数组
}
}
// 计算聚集表
let aggregateArr = [];
// aggregateVal 为聚合参数, 记录要聚合的列数
for (let aggregateVal = 0; aggregateVal <= effeLength; aggregateVal++) {
aggregate(aggregateArr, baseArr, aggregateVal)
}
结果图:
完整代码,可以复制到html文件中在浏览器运行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
基本表
<table id="baseTable" border="1px">
<tr>
<td colspan="3">Dimensions</td>
<td>Measure</td>
</tr>
<tr>
<td>Store</td>
<td>Product</td>
<td>Season</td>
<td>Sales</td>
</tr>
</table>
</div>
<div>
聚集表
<table id="aggregateTable" border="1px">
<tr>
<td colspan="3">Dimensions</td>
<td>Measure</td>
</tr>
<tr>
<td>Store</td>
<td>Product</td>
<td>Season</td>
<td>AVG(Sales)</td>
</tr>
</table>
</div>
</div>
</body>
<script>
// 基本数据 修改基本数据要修改table结构
let baseArr = [
['S1', 'P1', 'Spring', 6],
['S1', 'P2', 'Spring', 12],
['S2', 'P1', 'Fall', 9],
]
// 数据有效长度,因为sales不计入聚合
const effeLength = baseArr[0].length - 1;
// 将数据渲染为html
function addDom(table, list) {
let tr = document.createElement('tr'); // 创建元素tr
list.forEach(text => {
let td = document.createElement('td');
td.innerHTML = text;
tr.appendChild(td); // 将td挂到tr上
})
table.appendChild(tr); // 将新的一行数据挂到table上
}
// 聚合核心算法
function aggregate(aggregateArr, baseArr, aggregateVal) {
let obj = {}; // 使用hash来对相同数据去重(由于对象具有hash的作用,这里直接使用对象就不用map了
for (let i = 0; i < effeLength; i++) { // 遍历列 01 12 20
let copyArr = JSON.parse(JSON.stringify(baseArr)) // 深拷贝,防止后续处理污染原始数据
copyArr.forEach(v => {
let index = i; // 记录当前聚合列的索引
for (let j = 0; j < aggregateVal; j++) { // aggregateVal 的值代表聚合的列数
v[index] = '*'
index++;
if (index >= effeLength) {
index = 0 // 形成索引环 超过数据长度就从头开始
}
}
// 将将列合并,方便数据去重,合并相同项,并计算sales和与出现次数(用以计算平均值)
let key = '';
for (let j = 0; j < effeLength; j++) {
key += v[j] + ','
}
if (obj[key]) { // 数据如果已存在,则累加sales值,并记录出现次数
obj[key].value += v[effeLength]
obj[key].frequency += 1
} else { // 没有就新建一个
obj[key] = {} // obj= {key:{}} <==> obj.key = {}
obj[key].value = v[effeLength] // obj.key = {value: sales}
obj[key].frequency = 1 // obj.key = {value:sales,frequency:1}
}
});
}
for (let key in obj) {
let temp = key.split(','); // 分割出去重后的列
temp[effeLength] = obj[key].value / obj[key].frequency // 重新计算sales平均值
aggregateArr.push(temp) // 加入待渲染数组
}
}
// 渲染基础表
let baseTable = document.getElementById('baseTable')
baseArr.forEach(v => {
addDom(baseTable, v); // 渲染
})
// 计算聚集表
let aggregateArr = [];
// aggregateVal 为聚合参数, 记录要聚合的列数
for (let aggregateVal = 0; aggregateVal <= effeLength; aggregateVal++) {
aggregate(aggregateArr, baseArr, aggregateVal)
}
// 渲染聚集表
let aggregateTable = document.getElementById('aggregateTable')
aggregateArr.forEach(v => {
addDom(aggregateTable, v); // 渲染
})
</script>
<style>
#app {
display: flex;
flex-direction: row;
}
#app div:nth-child(2) {
margin-left: 20px;
}
</style>
</html>