最近需要实现一个统计对话中所出现的高频单词的功能,需要前端计算显示排名前四的高频词汇,这样的统计遵循两个条件:
首先按照单词出现的次数从大到小进行排序,然后在单词出现次数相同的情况下,最新出现的单词优先排在前面:
思考之后,我想出来的解决方案思路简单来说是,首先统计出每个单词的出现次数以及最后一次出现时的下标数,按照每个单词是出现次数进行第一轮排序,排序之后,根据单词出现次数对次数相同的单词再按照出现的下标的大小再进行一次排序:
所以就出现了我下面得到的 方法一:
function countArr(arr) {
const countObj = {};
arr.forEach(item => { // 统计数组当中每个元素的出现次数
if (!countObj[item]) {
countObj[item] = 1;
} else {
countObj[item] ++;
}
});
const countArr = [];
for (const key in countObj) { //获得每个元素出现次数与最后一下出现下标
countArr.push({name: key, count: countObj[key], index: arr.lastIndexOf(key)});
}
let sortType = 'count';
countArr.sort(sortByTwo); //第一次按照count值从大到小排序
countArr.sort((a,b) => {
return b[sortType] - a[sortType];
})
let maxCount = countArr[0].count;
let endNum = 0;
let sortedArr = [];
for (; maxCount > 0; maxCount--) {
startNum = findIndex(countArr, sortType, maxCount);
endNum = findLastIndex(countArr, sortType, maxCount);
let partArr = countArr.slice(startNum, endNum + 1); // 获取count值相同的子数组
if (partArr.length > 0) { //按照index大小进行排序
partArr.sort((a,b) => {
return b['index'] - a['index'];
})
}
sortedArr = sortedArr.concat(partArr); // 拼接数组
}
}
function findLastIndex(arr, key, value) { //获取最后一次出现的下标
let lastIndex = 0;
arr.forEach( (item,index) => {
if(item[key] === value) {
lastIndex = index;
}
})
return lastIndex;
}
function findIndex(arr, key, value) { //获取第一次出现的下标
let firstIndex = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i][key] === value) {
firstIndex = i;
break;
}
}
return firstIndex;
}
接下来对两个数组进行一次排序,执行下面的代码:
const arrTest = ['a', 'b', 'c', 'ab', 'ab', 'bc', 'aa', 'c'];
const arrTest2 = ['apple', 'banana', 'pear','tomato', 'apple', 'stawberry', 'watermelon','mango','apple', 'pear'];
countArr(arrTest);
countArr(arrTest2);
得到结果如下,可以看到两个数组执行的结果都是我们想要的:
此种方法虽然可以得到正确的l排序结果,但是考虑到里面用到了多次循环,且代码看起来有点复杂,可读性不高,后来研究一下就得到了一个直接用sort方法直接根据两个条件进行排序的 方法二, 即直接将sort方法优化成如下方式:
function sortByTwo(a, b) {
if (a.count === b.count) {
if (a.index > b.index) {
return -1;
} else if (a.index < b.index) {
return 1;
}
return 0
} else {
if (a.count > b.count) {
return -1;
}
if (a.count < b.count) {
return 1;
}
}
}
所以直接执行下面的代码,就可以得到方法一同样的排序结果:
function countArr(arr) {
const countObj = {};
arr.forEach(item => { // 统计数组当中每个元素的出现次数
if (!countObj[item]) {
countObj[item] = 1;
} else {
countObj[item] ++;
}
});
const countArr = [];
for (const key in countObj) { // 获取每个元素出现次数与下标的数组
countArr.push({name: key, count: countObj[key], index: arr.lastIndexOf(key)});
}
countArr.sort(sortByTwo); // 利用sort根据两个条件进行排序
}
function sortByTwo(a, b) {
if (a.count === b.count) {
if (a.index > b.index) {
return -1;
} else if (a.index < b.index) {
return 1;
}
return 0
} else {
if (a.count > b.count) {
return -1;
}
if (a.count < b.count) {
return 1;
}
}
}
const arrTest = ['a', 'b', 'c', 'ab', 'ab', 'bc', 'aa', 'c'];
const arrTest2 = ['apple', 'banana', 'pear','tomato', 'apple', 'stawberry', 'watermelon','mango','apple', 'pear'];
countArr(arrTest);
countArr(arrTest2);
上述方法二代码运行之后会得到和方法一同样的结果,但是代码却精简很多,可读性也更高。
但是如果是两个以上的更多条件的排序怎么办呢,考虑到其实我们可以按顺序吧排序条件放到一个数组里面,然后在排序的时候依次根据新的下标进行排序,所以将sort函数优化成了如下使用递归调用的方式:
const sortCondition = ['name', 'count', 'index']; //排序条件数组
function sortByArr(a, b, index) {
let i = index ? index : 0;
if (i === sortCondition.length - 1){
return;
}
if (a[sortCondition[i]] === b[sortCondition[i]]) {
if (i < sortCondition.length - 1) {
i++;
countArr.sort(sortByArr(a, b, i));
}
}
if (a[sortCondition[i]] > b[sortCondition[i]]) {
return -1;
}
if (a[sortCondition[i]] < b[sortCondition[i]]) {
return 1;
}
}
完整版方法三代码如下:
let countArr = []; // 定义排序后的数组
function sortArr(arr) {
const countObj = {};
arr.forEach(item => { // 统计数组当中每个元素的出现次数
if (!countObj[item]) {
countObj[item] = 1;
} else {
countObj[item] ++;
}
});
for (const key in countObj) { // 获取每个元素出现次数与下标的数组
countArr.push({name: key, count: countObj[key], index: arr.lastIndexOf(key)});
}
countArr.sort(sortByArr); // 利用sort根据排序条件数组进行排序
console.log(countArr);
}
const sortCondition = ['name', 'count', 'index']; // 排序条件数组
function sortByArr(a, b, index) {
let i = index ? index : 0;
if (i === sortCondition.length - 1){
return;
}
if (a[sortCondition[i]] === b[sortCondition[i]]) {
if (i < sortCondition.length - 1) {
i++;
countArr.sort(sortByArr(a, b, i));
}
}
if (a[sortCondition[i]] > b[sortCondition[i]]) {
return -1;
}
if (a[sortCondition[i]] < b[sortCondition[i]]) {
return 1;
}
}
const arrTest = ['a', 'b', 'c', 'ab', 'ab', 'bc', 'aa', 'c'];
sortArr(arrTest);
下面得到按三个条件排序后的结果: