I was interested in one of the questions, so I put my thinking process into words.

// destructuringArray([1,[2,4],3],"[a,[b],c]" );  
// result  
// { a:1, b:2, c:3 }
Copy the code

The author of the original text gives a solution, the implementation is more ingenious, and in the scene of improvisation, it is more rare, interested in the original text can look.

Now get this problem, have plenty of time to consider.

Start with the test case

[1, 2, 4], 3]."[a,[b],c]"= = > {2, a: 1, b: c: 3} [1, [2, 3, [4]]]."[react,[vue,rxjs,[angular]]]"= = > {RXJS react: 1, the vue: 2:3, presents: 4} [1, (2, 4), 3]."[a,[[b],c]"==> Error: brackets [1,[2,4],3],"[a,[b]],c]"==> brackets ==> stroustrup ==> stroustrup ==> stroustrup ==> stroustrup ==> stroustrup ==> stroustrup ==> stroustrup"[33a,[b],c]" ==> Error: key is invalid varible => 33a
[1,[2,4],3], "[re[a]ct,[b],c]"Error: invalid string==>pos:3 [1,[2,4],3],"rx,[react,[b],c]" ==> Error: template with invalid start or end
[1,[2,4],3], "[react,[b],c],rx" ==> Error: template with invalid start or end
Copy the code

The goal of this article is to run through the above cases.

As I explored this solution, I first thought about what the interviewer was looking for. Those who fail are those who can’t do it or give a wrong solution; Passing is getting the right result for a given use case; Even better, you can do basic checks on the input parameters; It is nice to be able to consider the vast majority of boundary cases, with the correct use case returning the correct result and the wrong use case throwing the corresponding exception.

Then it occurred to me that in the process of doing a project, whether writing business logic or encapsulating a common base library, I needed to be able to handle not only normal flow, but more importantly, exceptions that might be encountered. So there are fewer bugs, because bugs are caused by test engineers walking into a bar and asking for -1 beer. (Laughter)

I then thought of writing code or using templates (such as a traditional back-end template engine or front-end JSX) in a way that is not legal to write, and always get position-specific error messages. Marveling that writing great libraries and frameworks requires thinking about 10,000 different ways test engineers can walk into a bar in order to become popular.

But anyway, let’s go back to the problem. If we want to solve this problem in an excellent way, we need to take into account the abnormal situation that this problem may encounter and give specific error information.

There are three steps to deal with it:

Parse the template string (for example, “[a,[b],c]”), determine if it is a valid array string, give an error message if it is not, and return the parsed result if it is.

If the value of [1,2] is inconsistent with the value of [a,[b]], the value of [b] is in an array, and 2 is a number, the value of [b] is not in an array. If all go to the next step.

3. Output the result based on the array and the parsed result obtained in the first step.

The first step is parsing the array string

Let’s look at the first step of the code, this step is the most complex, need to deal with the array string of various exceptions, if the string is valid need to get the array parsing results.

Use “[react,[vue, RXJS,[angular]]]” as an example. The output contains two pieces of information, one is the position of the array in the string [[],[1],[1,2]]. (Simply put, there are three pairs of brackets in this example, and the position of each pair is determined by the depth of the brackets. For example, ‘[Angular]’ is the first position in the first layer. Search for the second location, record [1,2], the other is the location of the key, return a keyMap, here

KeyMap = {react: [0], vue: [1, 0], RXJS: [1, 1], presents: [0] 1}Copy the code

Once we have the location of the key, we can use the real ARR to fetch the key directly, such as Angular: arr[1][2][0].

So now the key and the difficulty is how to get this information. Here the author uses the following ideas:

1.

Iterate over a string of numbers,

2,

Only four cases are handled, left square brackets ([), right square brackets (]), commas (,), and other characters.

3,

Define a pointer index=0 and a key=”” for two local variables (index is used to record the current position, as opposed to the I in the traversal for loop, where index is the comma-separated position of each variable),

4,

(1) If it is a common character char, key+=char is used to spell key

(2) If it is a comma (,), it means that the variable key has been concatenated, and the position index should also be incremented by 1, and the key should be pushed into the keyList

(3) If the left square bracket ([) indicates that a new array is to be entered, then push the current index into the indexList and set index to 0, because the new array starts at 0 again. IndexList is then pushed into arrayPosList, which represents the current location of the new array.

1 pop out the last element in the indexList (the position at which it entered the current array) and assign the value to index. KeyMap [key] = […indexList, index]; keyMap[key] = […indexList, index]; IndexList is the location of the current array, plus index is the location of the key in the current array. Together, this is the location of the key in the entire array string. This step should also be added to step (2).

For example,in “[react,[vue, RXJS,[Angular]]]”, when the first character ‘[‘ is encountered, indexList.push(index),indexList=[0]; Indexlist.push (index),indexList=[0,1], set index to 0, and then add and add angular’s array brackets to change index to 2. Call indexlist.push (index), and the indexList becomes [0,1,2]

function parse(template) {
    let indexList = [],
        arrayPosList = [],
        data = {},
        keyList = [],
        key = ' ', index = 0, keyMap = {}, spaceReg = /\s/g; // Remove the space in the string template = template.replace(spaceReg,"");
    let len = template.length;

    for (let i = 0; i < len; i++) {
        let char = template[i];
        if (char == '[') { indexList.push(index); index = 0; Arrayposlist.push (indexList.slice(1)) arrayPosList.push(indexList.slice(1)) // ArrayPosList.push (indexList.slice(1))else if (char == '] ') {
            if(key ! = =' ') { keyList.push(key); keyMap[key] = [...indexList.slice(1), index]; // The 0th element in the indexList, 0, is the position of the first square bracket, which is redundant' ';
            }
            index = indexList.pop();
        } else if (char == ', ') {
            if(key ! = =' ') { keyList.push(key); keyMap[key] = [...indexList.slice(1), index]; // The 0th element in the indexList, 0, is the position of the first square bracket, which is redundant' ';
            }
            index++;
        } else {
            key += char;
        }
        prevChar = char;
    }
    console.log("arrayPosList==>",arrayPosList,"\nkeyMap==>", keyMap)
    return [arrayPosList, keyMap];
}
Copy the code

Parse (“[react,[vue, RXJS,[angular]]]”)

arrayPosList==> [ [], [ 1 ], [ 1, 2 ] ]
keyMap==> { react: [ 0 ],
  vue: [ 1, 0 ],
  rxjs: [ 1, 1 ],
  angular: [ 1, 2, 0 ] }
Copy the code

The structure of the array and the position of each variable are resolved correctly.

Handle exceptions

However, if the user’s input does not meet the criteria, Such as “[react, [vue]” (left bracket), “[the react, vue]]” (the right brackets), “[u] [the react, v e]” (brackets position disorder), “[33 react, @ vue]]” (variable illegal), and many other no processing, Below we give the code to handle the exception, the comment explains what kind of exception is handled

function parse(template) {
    let indexList = [],
        arrayPosList = [],
        data = {},
        keyList = [],
        key = ' ', index = 0, keyMap = {}, spaceReg = /\s/g; ********* template = template.replace(spaceReg,"");
    letlen = template.length; *********// Handle exceptionscase   "js,[react,vue]""[react,vue],js"* * * * * * * * *if(template[0] ! = ='['|| template[len - 1] ! = ='] ') {
        throw new Error('template with invalid start or end')}for (let i = 0; i < len; i++) {
        let char = template[i];
        if (char == '[') {
            letprevChar = template[i - 1]; *********// The left parenthesis must be preceded by '[',', 'or undefined, which corresponds to the position before the first left parenthesis in the array string, for example"[r[eact],vue]"* * * * * * * * *if(prevChar ! == undefined && prevChar ! = =', '&& prevChar ! ='[') {
                throw new Error('invalid string==>pos:'+i) } indexList.push(index); index = 0; Arrayposlist.push (indexList.slice(1)) arrayPosList.push(indexList.slice(1)) // ArrayPosList.push (indexList.slice(1))else if (char == '] ') {

            letnextChar = template[i + 1]; *********// The closing bracket must be followed by '] ', ', 'or undefined, which corresponds to the position after the last closing bracket in the array string. For example,"[react,[vu]e]"* * * * * * * * *if(nextChar ! == undefined && nextChar ! = =', '&& nextChar ! ='] ') {
                throw new Error('invalid string==>pos:'+i)
            }

            if(key ! = =' ') { keyList.push(key); keyMap[key] = [...indexList.slice(1), index]; // The 0th element in the indexList, 0, is the position of the first square bracket, which is redundant' '; } index = indexList.pop(); *********// If index is undefined, the indexList is empty, indicating that the right square brackets are more than the left square brackets, and the structure is incorrect *********if (index === undefined) {
                throw new Error('too many right square brackets ==>pos:' + i)
            }
        } else if (char == ', ') {
            if(key ! = =' ') { keyList.push(key); keyMap[key] = [...indexList.slice(1), index]; // The 0th element in the indexList, 0, is the position of the first square bracket, which is redundant' ';
            }
            index++;
        } else{ key += char; }} * * * * * * * * * / / if the indexList and elements, that left bracket than right brackets * * * * * * * * *if (indexList.length > 0) {
        throw new Error('too many left square brackets')} *********// Check the validity of js variables for the re *********let reg = /^(_|\$|[A-Za-z])(_|\$|\w)*$/
    keyList.forEach(key => {
        if(! reg.test(key)) { throw new Error('key is invalid varible => ' + key)
        }
    })

    console.log("arrayPosList==>",arrayPosList,"\nkeyMap==>", keyMap)
    return [arrayPosList, keyMap];
}
Copy the code

The logic of handling exceptions is relatively simple. The difficulty lies in whether all exception cases can be considered and processed accordingly.

The second step is to compare the structure of an array to an array string

The next thing to do is to get the structure of the array string and the location of all the keys, and we can easily get the value of the real array for the key.

Next, the real array ARR and array string structure is compared, the logic is relatively simple, the code is given directly below

/ / arr = [1, 2, 3, [4]]] template ="[react,[vue,rxjs,[angular]]]" ==parse(template)==>  arrayPosList = [ [], [ 1 ], [ 1, 2 ] ]
functionCheck (arr, arrayPosList) {check(arR, arrayPosList) {check(arR, arrayPosList) {check(arR, arrayPosList) {check(arR, arrayPosList) {check(arR, arrayPosList) {check(arR, arrayPosList) {Check (arR, arrayPosList) {Check (arR, arrayPosList) {Check (arR, arrayPosList); ForEach (item => {arr=[1,[2,3,4]], 4 corresponds to [angular].if (item.length == 0) return;
        let ret = arr;
        try {
            for (let i = 0; i < item.length; i++) {
                ret = ret[item[i]]
            }
        } catch (e) {
            throw new Error('invalid structure');
            return;
        }
        if(! Array.isArray(ret)) { throw new Error('invalid structure'); }})}Copy the code

The third step is to parse the obtained data

The last step is to get the value from the array based on the keyMap. After the previous parse and check steps, this step shows that arrays and array strings can be parsed.

functionDestructuringArray (arr, template) {// Necessary validationif(! Array.isArray(arr)) { throw new Error('invalid first argument'); } // Necessary validationif(typeof template ! = ='string') {
        throw new Error('invalid second argument'); } // The final result is returnedletret = {}; // Parse an array stringlet[arrayPosList, keyMap] = parse(template); // Check the structure of arrays and strings for match check(arr, arrayPosList); Keys (keyMap). ForEach (key => {let path = keyMap[key];
        let val = arr;
        for (leti = 0; i < path.length; I ++) {val = val[path[I]]} ret[key] = val}) console.log(ret,'= = = = = = = =')
    return ret;
}
Copy the code

If a string needs to parse ‘{‘ and’} ‘, and can handle null, undefined, true, false, and numeric values, the complexity increases by an order of magnitude. Can not help but sigh that the gap between themselves and god in light years.

In this paper, the