178 lines
5.6 KiB
Markdown
178 lines
5.6 KiB
Markdown
# 数组与字符串操作
|
||
|
||
## 1. 数组去重(多种方法)
|
||
|
||
> **场景**:标签列表去重、下拉选项去重、用户列表按 ID 去重、搜索历史去重。
|
||
> **解决**:数据列表中存在重复项影响展示或数据处理的问题。
|
||
|
||
```js
|
||
// 方法1: Set 自动去重,最简洁
|
||
const unique1 = arr => [...new Set(arr)];
|
||
|
||
// 方法2: filter + indexOf,只保留第一次出现的元素
|
||
const unique2 = arr => arr.filter((v, i) => arr.indexOf(v) === i);
|
||
|
||
// 方法3: reduce 累加,不存在才添加
|
||
const unique3 = arr => arr.reduce((acc, v) => acc.includes(v) ? acc : [...acc, v], []);
|
||
|
||
// 方法4: 对象数组按属性去重
|
||
const uniqueByKey = (arr, key) => {
|
||
const map = new Map(); // 用 Map 存储已出现的 key
|
||
arr.forEach(item => !map.has(item[key]) && map.set(item[key], item));
|
||
return [...map.values()]; // 返回去重后的值
|
||
};
|
||
```
|
||
|
||
## 2. 扁平化数组
|
||
|
||
> **场景**:多级分类菜单拍平为一维数组、树形结构转平铺列表、处理嵌套 JSON 数据。
|
||
> **解决**:嵌套数组不便于遍历、查找和渲染的问题。
|
||
|
||
```js
|
||
// 递归实现:支持指定展开深度
|
||
function flatten(arr, depth = Infinity) {
|
||
return arr.reduce((acc, val) => {
|
||
// 如果是数组且深度>0,递归展开
|
||
return acc.concat(
|
||
Array.isArray(val) && depth > 0 ? flatten(val, depth - 1) : val
|
||
);
|
||
}, []);
|
||
}
|
||
|
||
// 迭代实现(使用栈):避免递归栈溢出
|
||
function flattenIterative(arr) {
|
||
const stack = [...arr]; // 复制数组作为栈
|
||
const result = [];
|
||
while (stack.length) {
|
||
const item = stack.pop(); // 从栈顶取出
|
||
// 是数组则展开压回栈,否则加入结果
|
||
Array.isArray(item) ? stack.push(...item) : result.unshift(item);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 原生方法(ES2019+)
|
||
arr.flat(Infinity); // Infinity 表示完全展开
|
||
```
|
||
|
||
## 3. 字符串反转
|
||
|
||
> **场景**:回文判断预处理、文本效果处理、简单加密/混淆。
|
||
> **解决**:JS 没有内置字符串反转方法,需要手工实现。
|
||
|
||
```js
|
||
// 方法1:拆分成数组 -> 反转 -> 拼接
|
||
const reverse = str => str.split('').reverse().join('');
|
||
|
||
// 方法2:使用扩展运算符(支持 Unicode 字符)
|
||
const reverse2 = str => [...str].reverse().join('');
|
||
```
|
||
|
||
## 4. 判断回文
|
||
|
||
> **场景**:车牌号校验、特殊编码检测、文字游戏、算法题基础。
|
||
> **解决**:判断字符串正反读是否一致,常作为算法题输入校验或中间步骤。
|
||
|
||
```js
|
||
// 方法1:清理后比较反转结果
|
||
const isPalindrome = str => {
|
||
// 转小写并去除非字母数字字符
|
||
const clean = str.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||
return clean === clean.split('').reverse().join('');
|
||
};
|
||
|
||
// 方法2:双指针法(性能更优)
|
||
const isPalindrome2 = str => {
|
||
const s = str.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||
let left = 0, right = s.length - 1;
|
||
while (left < right) {
|
||
// 从两端向中间比较
|
||
if (s[left++] !== s[right--]) return false;
|
||
}
|
||
return true;
|
||
};
|
||
```
|
||
|
||
## 5. 最长公共前缀
|
||
|
||
> **场景**:搜索联想提取、路径匹配、文件名批量处理、自动补全功能。
|
||
> **解决**:批量字符串中提取共同前缀以进行分类或缩短展示。
|
||
|
||
```js
|
||
function longestCommonPrefix(strs) {
|
||
if (!strs.length) return '';
|
||
|
||
let prefix = strs[0]; // 以第一个字符串为初始前缀
|
||
for (let i = 1; i < strs.length; i++) {
|
||
// 逐步缩短前缀,直到它是当前字符串的前缀
|
||
while (strs[i].indexOf(prefix) !== 0) {
|
||
prefix = prefix.slice(0, -1); // 去掉最后一个字符
|
||
if (!prefix) return ''; // 前缀为空,无公共前缀
|
||
}
|
||
}
|
||
return prefix;
|
||
}
|
||
|
||
// 示例: ["flower","flow","flight"] => "fl"
|
||
```
|
||
|
||
## 6. 模板字符串解析
|
||
|
||
> **场景**:简易模板引擎、邮件/短信模板填充、国际化 i18n 文本替换。
|
||
> **解决**:将模板字符串中的占位符替换为实际数据,类似 Mustache/Handlebars 语法。
|
||
|
||
```js
|
||
// 基础版:支持 {{key}} 语法
|
||
function render(template, data) {
|
||
// \{\{(\w+)\}\} 匹配 {{xxx}},\w+ 捕获 key
|
||
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
||
return data[key] !== undefined ? data[key] : match; // 无值则保留原样
|
||
});
|
||
}
|
||
|
||
// 进阶版:支持嵌套属性 {{a.b.c}}
|
||
function renderDeep(template, data) {
|
||
return template.replace(/\{\{(.+?)\}\}/g, (match, path) => {
|
||
const keys = path.trim().split('.'); // 按 . 拆分路径
|
||
let value = data;
|
||
for (const key of keys) {
|
||
value = value?.[key]; // 逐层取值,可选链防止报错
|
||
if (value === undefined) return match;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
|
||
// 使用示例
|
||
render('Hello, {{name}}!', { name: 'World' }); // "Hello, World!"
|
||
renderDeep('{{user.info.name}}', { user: { info: { name: 'Tom' } } }); // "Tom"
|
||
```
|
||
|
||
## 7. 数组按属性去重并排序
|
||
|
||
> **场景**:用户列表按 ID 去重后按注册时间排序、商品列表按 SKU 去重后按价格排序。
|
||
> **解决**:对象数组的去重和排序组合操作,常见业务需求。
|
||
|
||
```js
|
||
function uniqueAndSort(arr, key, sortKey, order = 'asc') {
|
||
// 第一步:按指定 key 去重
|
||
const map = new Map();
|
||
arr.forEach(item => !map.has(item[key]) && map.set(item[key], item));
|
||
|
||
// 第二步:按指定 sortKey 排序
|
||
return [...map.values()].sort((a, b) => {
|
||
return order === 'asc'
|
||
? a[sortKey] - b[sortKey] // 升序
|
||
: b[sortKey] - a[sortKey]; // 降序
|
||
});
|
||
}
|
||
|
||
// 示例
|
||
const users = [
|
||
{ id: 1, name: 'Tom', age: 20 },
|
||
{ id: 2, name: 'Jerry', age: 18 },
|
||
{ id: 1, name: 'Tom2', age: 25 } // id 重复,会被过滤
|
||
];
|
||
uniqueAndSort(users, 'id', 'age'); // 按 id 去重,按 age 升序排序
|
||
```
|