要求:统计数据库某一列中字段的重复次数,即词频,并在前端以a-tag标签的形式展示词频最高的前n项。
一、后端部分
假设数据库中存在一列keywords,如下:
id | keywords |
---|---|
1 | aa,b,cc |
2 | aa,cc |
3 | b |
4 | aa,d |
每一行代表一条数据,数据的keywords中,不同的keywords用符号(我这里就用的中文逗号)做分割,我要得到aa,b,cc,d这几个元素出现的次数,并按词频从高到低排序,即得到下图:
aa | 3 |
b | 2 |
cc | 2 |
d | 1 |
lets do it~
分析:利用HashMap解决,其中aa,b,cc,d作为map中的key,其对应出现的次数作为map中的value,按照value值从高到低输出。
idea中springboot文件目录如下,我个人理解的顺序是这样:数据库数据-->entity层-->service层下impl的类-->service层的接口-->controller层与前端交互。
1.entity层,定义一个实体类FaultHanding存放数据库中对应的字段,其中faulthanding_info是数据表的名字,id和keywords分别是表中的两个字段
@TableName(faulthanding_info)
@Data
@EqualsAndHashCode(callSuper=true)
public class FaultHanding extends BaseEntity{
@TableId(value="id",type=IdType.ASSIGN_ID)
private Long id;
@TableField(value="keywords")
private String keywords
}
2.service层,读取所有的keywords,使用HashMap的内置方法,得到每个字符对应的出现次数,将得到的map转化成list?使用sort排序,最后得到按照词频从高到低排序后的列表。可以参看代码的注释,有不对的地方可以指正。
@Service
public class FaultHandingServiceImpl extends ServiceImpl<FaultHandingMapper,FaultHanding> implements FaultHandingService {
public List<Map.Entry<String,Integer>> keywordMap(){
//把数据库中的所有数据都读出来,此时querywrapper是list类型,存的数据类似{[id1,keywords1],[id2,keywords2],[id3,keywords3],[id4,keywords4],[id5,keywords5]}
LambdaQueryWrapper<FaultHanding> queryWrapper = new LambdaQueryWrapper<>();
List<String> keywordList = CollectionUtil.newArrayList();
//有的数据的keywords是很多个,用中文逗号分割的,首先要读取数据库字段keywords下所有的数据,即把aa、b、cc、d都读出来,所以要foreach遍历querylist列表
this.list(queryWrapper).forEach(faultHanding -> {
//先把含多个关键词的项按标点符号分割出来,例如分割前“aa,b,cc”这三项是一个字符串,使用split方法后,就把“aa,b,cc”这个字符串分成了“aa,b,cc”,分割后是三个字符串,aa、b、cc都是独立的,把每条数据的keywords都分割出来后,循环存放到list列表,此时list列表是没有去重复项的,list里的内容是这样的:["aa,b,cc","aa,cc","b","aa,d"]。
List<String> list = CollectionUtil.newArrayList(faultHanding.getKeywords().split(","));
list.forEach(e -> {
//循环遍历list列表["aa,b,cc","aa,cc","b","aa,d"],使用add方法存储到keywordList里,得到的keywordList是这样的["aa","b","cc","aa","cc","b","aa","d"]
keywordList.add(e);
});
});
//定义一个map,字符串为key,出现次数为value。
Map<String, Integer> map = new HashMap<>();
//遍历keywordList["aa","b","cc","aa","cc","b","aa","d"],此时的map是空的,所以先执行else中的语句。
keywordList.forEach(i ->{
//如果map中包含i,map.get(i)得到value即该字符出现的次数,让其+1,使用push方法存到map中。
if(map.containsKey(i)){
map.put(i,map.get(i)+1);
}else{
//map最开始为空,并不存在keywordList中的字符,所以keywordList中的每一项在map第一次遍历的时候,就作为map的key存到了map中,因此map的key是不重复的,其对应的value都是1,就像aa=1,b=1,cc=1,d=1
map.put(i,1);
}
//到此就得到了一个无序的map,里边存储了字符及其出现的次数,接下来对其从高到低排序
});
//定义一个flag为false作为下边的判断条件,这块我参看网上的也不太清楚为什么,但是能运行出来。
final boolean flag=false;
//定义一个新的列表info,搜map.entrySet()能看到info的定义是一种固定写法?通过getKey()得到K,getValue得到V
List<Map.Entry<String,Integer>> info =new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
//使用Comparator对info进行排序,sort自动帮忙排序
Collections.sort(info, new Comparator<Map.Entry<String, Integer>>() {
//重写了compare方法,我参考别人的,也不太懂,但他写的对。
public int compare(Map.Entry<String, Integer> obj1, Map.Entry<String, Integer> obj2) {
if (obj2.getValue() - obj1.getValue() != 0) {
return obj2.getValue() - obj1.getValue();
} else {
if (flag) {
return obj1.getKey().compareTo(obj2.getKey());
} else {
return -(obj1.getKey().compareTo(obj2.getKey()));
}
}
}
});
//得到的info就是按value排序后的列表,接下来把列表传到前端即可
return info;
}
}
3.service层接口
public interface FaultHandingService extends IService<FaultHanding>{
List<Map.Entry<String,Integer>> keywordMap();
}
4.controller层
@RestController
@ApiResource(name = "故障知识库", path = "/faultHanding")
public class FaultHandingController {
@GetResource(name="获取关键词列表",path="/keywordMap", requiredLogin = false, requiredPermission = false)
public ResponseData keywordMap(){
return new SuccessResponseData(faultHandingService.keywordMap());
}
到这里后端的内容就写好了,前端使用vscode
首先需要写一个js作为api,和后端的controller方法对应
代码如下,方法名要和controller的方法名一样,我这里都是keywordMap:
import Request from '@/utils/request';
export class faultHandingApi {
static keywordMap(params){
return Request.getAndLoadData('/faultHanding/keywordMap', params);
}
}
前端使用a-tag标签,把内容做展示,新建一个vue,我的项目中要展示11项内容,所以slice方法写的11,根据情况自己修改就可以
<template>
<div style="text-align:center;margin-top:2%" >
<a-space>
<div v-for="(item, index) in keywordList.slice(0,11)" :key="index">
<a-tag >
<a>{{ Object.keys(item)[0]}}</a>
</a-tag>
</div>
</a-space>
</div>
</template>
<script>
import { faultHandingApi } from './faultHandingApi';
export default defineComponent({
data() {
return {
keywordList:[],
}
},
async created(){
this.keywordList=await faultHandingApi.keywordMap();
},
methods:{
},
})
</script>
由于我开发项目具有一定的保密性,因此数据库内容和文章中举例有不同,但方法是一样的,供大家参考,有问题一起探讨,不对的地方请指正,最后的效果图如下:搜索框下边的11个标签就是数据库中词频最高的几项