3

一道前端面试题

 2 years ago
source link: https://feizhaojun.com/?p=3378
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一道前端面试题

分类: ES6, JavaScript作者: mukti  时间: 2022-01-17 17:06:55 (星期一)

输入字符串’a(3)b(1)cde(2)f(0)’,得到输出结果:’aaabcdee’。

其中括弧内的数字是括弧前面字母的重复次数;如果字母后面没有括弧,就原样输出(相当于重复1次);括弧内数字为0则不输出。

这道题其实非常简单,但是有多种解题思路,涉及的基本知识点也很多。下面我会按照我觉得由低到高的方式一一列举出来。

第一步当然是把字符串放到变量 str 里面,下文我们都会用这个变量:

let str = 'a(3)b(1)cde(2)f(0)';

1. 最基本的方法

最基本的方法就是遍历这个字符串。

如何遍历字符串?

字符串其实是有下标的,所以可以直接用for循环:

for (let i = 0; i < str.length; i++) {
    // 这里 str[i] 便可以拿到每个字符
}

抽象暂存数据

这个题目最重要的一步,是要在输出所需字符串之前,抽象出规则,暂存数据以备我们拼接成输出字符串。

这里我们把所需数据放到一个数组tempArr里面,遍历字符串时,遇到字母或数字就将其放到这个数组:

let tempArr = [];

tempArr 中,我们期望每一个元素的格式是这样的:

{
    charactor: 'a',
    repeat: 3
}

这样我们抽象出了一份数据,数据中包含了输出的字母 charactor 和需要重复的次数 repeat。

如何判断字母或数字?

这个字符串中,我们只要判断 str[i] 不等于 () 就好了,当然,为了安全,我们还是要校验字母和数字,大家一定可以想到用正则表达式,那如果不用正则呢?

校验字母就需要把26个字母和10个数字拼成字符,然后用 indexOf 来判断。

判断是不是数字,也可以用 isNaN,在给定字符串中,isNaN(str[i]) === false 则代表 i 位是数字。

当然,为了逻辑严谨,我们还要考虑括号前的字符可能不仅仅是字母怎么办。如果括弧内不是数字还要做容方案。

使用正则判断字母和数字使用 [a-z]\d,不再赘述。

上面我们已经写了一个for循环,为了让代码更为“优雅”,我们也可以将字符串分割为数组,然后使用 forEach

str.split('').forEach((el, i) => {
    // 声明一个本轮循环的变量
    let temp = {};
    if(/[a-z]/.test(str[i])) {
        // 如果是字母
        // 将字母放入抽象数据
        temp.charactor = str[i];
        if (str[i+1] === '(') {
            // 判断字母后面的字符是什么
            // 如果字母后面是左括弧,那重复次数就是该字母后2位
            temp.repeat = parseInt(str[i+2]);
        } else {
            // 字母后面不是左括弧,那就是省掉次数的字母(或最后一位是字母),如给定字符串中的 c、d,这时候其实 repeat 就是 1
            temp.repeat = 1;
        }
        // 把本轮抽象出来的数据push到上文已声明的数组
        tempArr.push(temp);
    }
    // 如果不是字母(括弧或数字)不做任何处理继续往后遍历
});

通过上面的处理,我们就可以得到这样的一个数据:

[
    { charactor: 'a', repeat: 3 }
    { charactor: 'b', repeat: 1 }
    { charactor: 'c', repeat: 1 }
    { charactor: 'd', repeat: 1 }
    { charactor: 'e', repeat: 2 }
    { charactor: 'f', repeat: 0 }
]

我们只需要循环上面的数组,就可以得到需要输出的字符串了。

多余的括弧

我们其实可以发现,给定字符串中的左右括弧,完全是干扰项。我们可以第一步就把这些括弧去掉,把给定字符串由 a(3)b(1)cde(2)f(0) 变为 a3b1cde2f0,这会让给定字符串看起来更清晰,但是用处不大。

使用map()

我们抽象出一个数据结构之后,再拼装为所需要输出的字符串。这一步没有很复杂,所以我们也可以放在循环里面,然后使用 map 替换 forEach 来处理,最后通过 join() 转为输出字符串:

let newStr = str.split('').map((el, i) => {
    let tempStr = '';
    if(/[a-z]/.test(el)) {
        if (str[i+1] === '(') {
            for (let j = 0; j < parseInt(str[i+2]); j++) {
                // 根据数字循环字母
                tempStr += el;
            }
        } else {
            tempStr = el;
        }
    }
    return tempStr;
}).join('');
console.log(newStr);

2. 更妙的方法

这个题目更好的方式是用正则表达式,从一个字符串到另一个字符串,我们只需要用到字符串的 replace 方法。

这其中最关键的点,是如何用正则匹配出一组一组的数据,在给定字符串中,可以划分为:’a(3)’,’b(2)’,’c’,’d’……这样的几组,需要根据这些写出一个匹配的正则表达式:/(\w)\((\d)\)/

把正则表达式作为第一个参数给 replace,第二个参数使用一个处理函数返回需要的内容即可。这样我们只需要1行代码来解决这个问题,大家可以体会下:

'a(3)b(1)cde(2)f(0)'.replace(/(\w)\((\d)\)/g, (match, $1, $2) => Array(+$2).fill($1).join(''));

这一行中包括了:正则 replace、数组长度、字符串转数字、fill()、join() 等知识点。

领支付宝红包:打开支付宝搜索 726845401

领饿了么红包:0fυィ直文本 e:/$XGzEgR8$~.👉饿了么App👈【快來領外賣紅包,最高20元,人人都有哦~】

您的赞助将会鼓励作者技术文章创作以及支持本站运维。

Follow any responses to this post with its comments RSS feed. You can post a comment or trackback from your blog.
Your email is never published nor shared. Required fields are marked *
Name *
Email *
Website
评论

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK