需求描述
- 前兩天和以前公司的同事聊天,他提到在刷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...