需求描述
- 前两天和以前公司的同事聊天,他提到在刷leetcode,并提到算法感觉不接地气
- 似乎也没有什么应用场景
- 于是乎,笔者翻出一个案例需求给到他
- 看完以后,他感叹道:
- 还是自己见得少了...
需求:假设有一个时间线组件,按照时间顺序展示对应内容,如下图
- 这个时间线可能很长,能有好几个月
- 为了提高用户体验,当用户进入这个页面的时候
- 我们需要看看当天是哪一天,同时滚动到,距离时间线节点的最近的那一天
- 省的用户再去滑动了
- 这个问题需求,可以抽象成为一个算法题目
- 数组中寻找与某个数最近的那一项
效果图
假设今天是25年1月16日,所以,应该滚动到 内容666、2025-01-16日哪一项
- 滚动我们知道,使用锚点即可
- 但是我们需要知道如何滚动到最近的一天
- 所以,题目抽象成一道简单的入门算法题
- 查找最近的项
问题简化算法
- 假设有一组数组,为
let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
- 现在出来一个任意数字,假设是
51
- 我们需要找距离这个数字最近的一项是哪一项
解法一 循环求差值数组,并排序取最小的那一项
let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let an = 51
function findRecentNumInfo(arr, an) {
// 定义数组,求出每个值与目标值的差值,并存储起来
let infoArr = []
for (let i = 0; i < arr.length; i++) {
// 这里存储的是对象,包含索引、值、差值
infoArr.push({
index: i,
value: arr[i],
// 差值可能是负数,所以取绝对值
diff: Math.abs(arr[i] - an)
})
}
// 再排序一下,找到差值最小的,即为第0项,第一个
infoArr.sort((a, b) => a.diff - b.diff)
// 正序排序完毕,把第一项给返回出去即可
return infoArr[0]
}
console.log(findRecentNumInfo(arr, an));
- 上述解法,相当于循环了两次
- 取差值一次,排序一次
- 事实上,我们还有更优解
- 只要循环一次即可
解法二 单次循环比较差值大小,并直接取最小项
let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let an = 51
function findRecentNumInfo(arr, an) {
// 这里存储的是对象,包含索引、值、差值
let info = {
index: +Infinity,
value: +Infinity,
diff: +Infinity,
};
// 通过对比,若差值小于之前的差值,则更新差值和索引对应项
for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i] - an) < info.diff) {
info.diff = Math.abs(arr[i] - an);
info.index = i
info.value = arr[i]
}
}
// 一次遍历完毕,即可找到距离最近的对应项,返回出去即可
return info
}
console.log(findRecentNumInfo(arr, an));
毫无疑问,这种方式为更优解
代码示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let an = 51
/**
* 方式一:直接对比,找到最接近的值
* */
function findRecentNumInfo(arr, an) {
// 这里存储的是对象,包含索引、值、差值
let info = {
index: +Infinity,
value: +Infinity,
diff: +Infinity,
};
// 通过对比,若差值小于之前的差值,则更新差值和索引对应项
for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i] - an) < info.diff) {
info.diff = Math.abs(arr[i] - an);
info.index = i
info.value = arr[i]
}
}
// 一次遍历完毕,即可找到距离最近的对应项,返回出去即可
return info
}
/**
* 方式二:先存储差值数组,再找到最近的值
* */
// function findRecentNumInfo(arr, an) {
// // 定义数组,求出每个值与目标值的差值,并存储起来
// let infoArr = []
// for (let i = 0; i < arr.length; i++) {
// // 这里存储的是对象,包含索引、值、差值
// infoArr.push({
// index: i,
// value: arr[i],
// // 差值可能是负数,所以取绝对值
// diff: Math.abs(arr[i] - an)
// })
// }
// // 再排序一下,找到差值最小的,即为第0项,第一个
// infoArr.sort((a, b) => a.diff - b.diff)
// // 正序排序完毕,把第一项给返回出去即可
// return infoArr[0]
// }
console.log(findRecentNumInfo(arr, an));
</script>
</body>
</html>
vue
- 应用到需求上就是
- 找到最近的时间线组件的dom节点
- 执行
el.scrollIntoView({ behavior: "smooth" });
即可 - 以下的
{ content: "内容111", timestamp: "2025-01-11", }
- 大家可以自行更改时间即可
- 复制粘贴即用
<template>
<div class="box">
<el-timeline style="max-width: 600px">
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
>
{{ activity.content }}
</el-timeline-item>
</el-timeline>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const activities = ref([
{
content: "内容111",
timestamp: "2025-01-11",
},
{
content: "内容222",
timestamp: "2025-01-12",
},
{
content: "内容333",
timestamp: "2025-01-13",
},
{
content: "内容444",
timestamp: "2025-01-14",
},
{
content: "内容555",
timestamp: "2025-01-15",
},
{
content: "内容666",
timestamp: "2025-01-16",
},
{
content: "内容777",
timestamp: "2025-01-17",
},
{
content: "内容888",
timestamp: "2025-01-18",
},
{
content: "内容999",
timestamp: "2025-01-19",
},
{
content: "内容10",
timestamp: "2025-01-20",
},
{
content: "内容11",
timestamp: "2025-01-21",
},
{
content: "内容12",
timestamp: "2025-01-22",
},
{
content: "内容13",
timestamp: "2025-01-23",
},
]);
onMounted(() => {
scrollFn(1);
addTimestamp();
});
const addTimestamp = () => {
// 给每一项都添加时间戳
activities.value.forEach((item) => {
item.s = new Date(item.timestamp).getTime();
});
calRecent();
};
const calRecent = () => {
let now = new Date().getTime();
let res = findRecentInfo(activities.value, now);
let n = res.index + 1;
setTimeout(() => {
scrollFn(n);
}, 2000);
};
const findRecentInfo = (arr, an) => {
// 存一下差值,看谁的距离近一些
let info = {
index: +Infinity,
value: +Infinity,
diff: +Infinity,
};
// 通过对比,若差值小于之前的差值,则更新差值和索引(直接找到差值最小的)
for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i].s - an) < info.diff) {
info.diff = Math.abs(arr[i].s - an);
info.index = i;
info.value = arr[i];
}
}
return info;
};
const scrollFn = (n) => {
const el = document.querySelector(`.el-timeline-item:nth-child(${n})`);
el.scrollIntoView({ behavior: "smooth" });
};
</script>
<style>
.box {
box-sizing: border-box;
zoom: 2;
padding: 12px 0px;
}
</style>
A good memory is better than a bad pen. Record it down...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。