Advanced-Frontend / Daily-Interview-Question

我是依扬(木易杨),公众号「高级前端进阶」作者,每天搞定一道前端大厂面试题,祝大家天天进步,一年后会看到不一样的自己。

Home Page:https://muyiy.cn/question/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

第 152 题:实现一个 normalize 函数,能将输入的特定的字符串转化为特定的结构化数据

gauseen opened this issue · comments

字符串仅由小写字母和 [] 组成,且字符串不会包含多余的空格。
示例一: 'abc' --> {value: 'abc'}
示例二:'[abc[bcd[def]]]' --> {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}

commented
let normalize = str => {
  let result = {}
  let c
  
  // 字符串转化为数组
  let arr = str.split(/[\[\]]/g).filter(Boolean)
  
  // 生成结构化数据
  arr.forEach((item, index) => {
    if(index != 0) {
      c.children = {}
      c.children.value = item
      c= c.children
    } else {
      result.value = item
      c= result
    }
  })
  
  return result
}
let str = '[abc[bcd[def]]]'
normalize(str)
// {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}
const normalize = (str) => {
  var result = {}
  str.split(/[\[\]]/g).filter(Boolean).reduce((obj, item, index, a) => {
    obj.value = item
    if(index !== a.length -1) {
      return (obj.children = {})
    }
  }, result)
  return result
}
function normalize(str) {
	let arr = str.match(/\w+/g)
	let temp = {}
	let obj
	while(arr.length) {
		let item = arr.pop()
		temp.value = item
		obj && (temp.children = obj)
		if(arr.length) {
			obj = {...temp}
			temp = {}
		}else {
			obj = temp
		}
	}
	return obj
}
const normalize = str => {
  const arr = str.split(/[\[\]]/).filter(Boolean);
  const createArr = arr => {
    return arr.reduce((obj, val, n, arr) => {
      arr.length > 1 ? (obj.children = createArr(arr.slice(1))) : "";
      obj.value = arr[0];
      return obj;
    }, {});
  };
  return createArr(arr);
};
var normalize = (str) => {
  var list = str.match(/\w+/g)
  var obj = {}
  var curr = obj
  while (key = list.shift()) {
      curr.value = key
      if (list.length === 0) break
      curr.children = {}
      curr = curr.children
  }
  return obj
}

字符串仅由小写字母和 [] 组成,且字符串不会包含多余的空格。
示例一: 'abc' --> {value: 'abc'}
示例二:'[abc[bcd[def]]]' --> {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}

let normalize = (str) => {
    let arr = str.split(/[\[\]]/).filter(Boolean);
    let i = 0, obj = {}, cur = obj;
    while(i < arr.length) {
        if (i > 0) cur = cur.children = {};
        cur.value = arr[i];
        i ++;
    }

    return obj;
}
commented
function normalize(str, res = {}) {
  if (str) {
    if (str[0] === "[") {
      const exec = /^\[(\w+)(\[.*\]){0,1}\]$/.exec(str);
      if (exec) {
        const [, value, next] = exec;
        res.value = value;
        next && normalize(next, (res.children = {}));
      }
    } else if (!/(\[|\])/.test(str)) {
      res.value = str;
    }
  }
  return res;
}

// 测试用例
[
  "[a[b[c]]]",
  "",
  "asd",
  "[aads",
  "asdasd]",
  "[asddd]",
  "[a[d]",
  "[s[]]"
].reduce((acc, c) => ((acc[c] = normalize(c)), acc), {});

es5、纯算法:

function normalize(str) {
  if (!/(\[|\])/.test(str)) {
    return { value: str };
  }
  var len = str.length,
    quene = [],
    res = {},
    ret = res,
    current = "";
  for (var i = 0; i < len; i++) {
    var c = str[i];
    if (c === "[" || c === "]") {
      if (current) {
        quene.push(current);
        current = "";
      }
      if (c === "]") {
        var top = quene.shift();
        if (top) {
          res.value = top;
          if (quene.length) {
            res = res.children || (res.children = {});
          }
        }
      }
    } else {
      current += c;
    }
  }
  return ret;
}
function normalize(str) {
  if (!str) return {};
  const arr = str
    .split(/[\[\]]/g)
    .filter(Boolean)
    .reverse();
  return arr.reduce((acc, cur) => {
    const temp = {};
    if (acc.value) { //说明此时acc !== {}
      temp.children = acc;
    }
    temp.value = cur;
    return temp;
  }, {});
}
function normalize(str) {
  let current, result;
  while (current = str.match(/\[([a-z]+)\]/)) {
    let [res, value] = current;
    result = result ? { value, children: result } : { value };
    str = str.replace(res, '');
  }
  if (!result) result = {value: str};
  return result;
}
commented
function normalize(str) {
      const list = str.match(/\w+/g)
      function fn(list) {
        if(list.length === 1){
          return {
            value: list[0]
          }
        }
        let res = {}
        const item = list.shift();
        res = {
          value: item,
          children: fn(list)
        }
        return res
      }
      return fn(list)
 }
var str = '[abc[bcd[def]]]';
console.log(fn(str));
function fn(strs) {
	var arr = strs.replace('[', '').replace(/]/g, '').split('[');
	// console.log('arr: ', arr);
	var res = {};
	var pointer = res;
	for (var i = 0; i < arr.length; i ++) {
		pointer.value = arr[i];
		if (i < arr.length - 1) {
			pointer.children = {};
			pointer = pointer.children;
		}
	}
	return res;
}
function normalize(s) {
    let arr = s.match(/\w+/g)
    let result
    while(arr.length) {
        let cur = arr.pop()
        let temp = {value: cur}
        if(result) {
            temp.children = result
        }
        result = temp
    }
    return result
}


// 测试
var s = 'abc'
normalize(s) // {value: "abc"}

s = '[abc[bcd[def]]]'
normalize(s) 
// {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}

更过编程算法题可见 JavaScript-Algorithms

 return (obj.children = {})

return (obj.children = {}) 这个啥意思 呀 还是看不懂 为啥

 return (obj.children = {})

return (obj.children = {}) 这个啥意思 呀 还是看不懂 为啥

简化一下代码,你可以这么理解:

const normalize = (str) => {
  var result = {};
  str
    .split(/[\[\]]/g)
    .filter(Boolean)
    .reduce((obj, item) => {
      obj.value = item;
      let children = {};
      obj.children = children;
      return children;
    }, result);
  return result;
};
commented
 return (obj.children = {})

return (obj.children = {}) 这个啥意思 呀 还是看不懂 为啥

简化一下代码,你可以这么理解:

const normalize = (str) => {
  var result = {};
  str
    .split(/[\[\]]/g)
    .filter(Boolean)
    .reduce((obj, item) => {
      obj.value = item;
      let children = {};
      obj.children = children;
      return children;
    }, result);
  return result;
};

代码写的6,顶一个,但是用这种极简的*操作写法是为了减少代码量么,可读性不考虑么😅

const normalize = str => {
  const arr = str.split(/[\[\]]/).filter(Boolean);
  const createArr = arr => {
    return arr.reduce((obj, val, n, arr) => {
      arr.length > 1 ? (obj.children = createArr(arr.slice(1))) : "";
      obj.value = arr[0];
      return obj;
    }, {});
  };
  return createArr(arr);
};

你用了递归,你的reduce没有任何意义, 但是我没有证据

commented
const normalize = function(str) {
    let res = {}
    str.split(/[\[\]]/).filter(Boolean).reduce((prev, curr, index) => {
        if (index) prev = prev['children'] = {}
        prev['value'] = curr;
        return prev
    }, res)

    return res
}
commented
const normalize = (str = '') =>
  str
    .split(/[\[\]]/g)
    .filter(Boolean)
    .reverse()
    .reduce((acc, cur, index) => (!index ? { value: cur } : { value: cur, children: acc }), {});

let str1 = '[abc[bcd[def]]]',
  str2 = 'abc';
console.log(normalize(str1));
console.log(normalize(str2));

正则真香

const normalize = (str) => {
    const result = {}
    str.match(/(?=\w)\w+/g).reduce((total,current,index,arr)=>{
        total.value = current
        if(index < arr.length -1) {
            return (total.children = {})
        }
    },result)
    return result
}

都在正则,来个不用正则的:

/**
 * 第 152 题:实现一个 normalize 函数,能将输入的特定的字符串转化为特定的结构化数据
 * 字符串仅由小写字母和 [] 组成,且字符串不会包含多余的空格。
 * 示例一: 'abc' --> {value: 'abc'}
 * 示例二:'[abc[bcd[def]]]' --> {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}
 *
 * @version 2020-8-11
 */

const CHILDREN_START = Symbol();
const CHILDREN_END = Symbol();
const VALUE = Symbol();
const EOF = Symbol();

function scanner(s) {
  let pos = 0;
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      let token = {
        value: "",
        type: EOF,
        done: true,
      };
      for (; pos < s.length; ) {
        let c = s.charAt(pos++);
        switch (c) {
          case "'":
            continue;
          case "[":
            token.value = "[";
            token.type = CHILDREN_START;
            token.done = false;
            return token;
          case "]":
            token.value = "]";
            token.type = CHILDREN_END;
            token.done = false;
            return token;
          default:
            token.type = VALUE;
            token.done = false;
            token.value += c;
            while (pos < s.length) {
              c = s.charAt(pos++);
              if (["'", "[", "]"].includes(c)) {
                pos--;
                break;
              }
              token.value += c;
            }
            return token;
        }
      }
      return token;
    },
  };
}

function normalize(str) {
  const scan = scanner(str);
  let obj = {};
  let temp = obj;
  for (let v = scan.next(); !v.done; v = scan.next()) {
    switch (v.type) {
      case CHILDREN_START:
        temp.children = {};
        temp = temp.children;
        break;
      case VALUE:
        temp.value = v.value;
        break;
    }
  }
  if (!obj.value) {
    obj = obj.children;
  }
  return obj;
}

console.log(JSON.stringify(normalize("[abc[bcd[def]]]"))); // => {"value":"abc","children":{"value":"bcd","children":{"value":"def"}}}
console.log(JSON.stringify(normalize("abc"))); // => {"value":"abc"}
/**
 * 递归版本
 * @param {string} str 
 */
export function normalizeRecur(str) {
  const matchRe = /^\[.*\]$/
  const valueRe = /^\[([^\[\]]*)/
  let root = {}
  if (matchRe.test(str)) {
    root.value = str.match(valueRe)[1]
    root.children = normalize(str.slice(1 + root.value.length, str.length - 1))
  } else if (str.length > 0) {
    root.value = str
  } else {
    root = undefined
  }
  return root
}
/**
* 非递归版本
 * @param {string} str 
 */
export function normalize(str) {
  const stack = []
  const  startRe = /^\[/
  const endRe = /^\]/
  const valueRe = /^\[([^\[\]]*)/
  let root
  let parent
  while(str) {
    if(startRe.test(str)) {
      let valueMatch = str.match(valueRe)
      let cur = {}
      root = root || cur
      if (parent) parent.children = cur
      cur.value = valueMatch[1]
      stack.push(cur)
      parent = cur
      str = str.slice(valueMatch[0].length)
    } else if (endRe.test(str)) {
      str = str.slice(1)
      stack.pop()
    }
  }
  return root
}
function normalize(str) {
  var array = str.match(/[a-z]+/g);
  var obj = {};
  array.reduce((acc, cur, index) => {
    const isInLast = index == array.length - 1;
    acc.value = cur;
    if (!isInLast) {
      acc.children = {}
    }
    return isInLast ? acc : acc.children
  }, obj);
  return obj;
}

normalize('abc')
normalize('[abc[bcd[def]]]')
commented
function normalize(str) {
    let len = str.length, stack = [], j = 0
    for (let i = 0; i < len; i++) {
        if (str[i] === '[') {
            j++
        } else if (str[i] === ']') {
            j--
        } else {
            if (!stack[j]) {
                stack[j] = ''
            }
            stack[j] = stack[j] + str[i]
        }
    }
    if (stack.length && stack[0] === undefined) {
        stack.shift()
    }
    let root = {}, temp = root
    for (let i = 0; i < stack.length; i++) {
        temp.value = stack[i]
        if (i !== stack.length - 1) {
            temp.children = {}
            temp = temp.children
        }
    }
    return root
}
function normalize(str) {
    let arr = str.split(/[\[\]]/g).filter(Boolean);
    if(arr.length === 1) return {value:arr[0]}
    return arr.reduceRight(function(pre, curr, idx) {
        pre.value = curr
        return idx > 0 ? {chiledren: pre} : pre
    }, {})
}

我想说,以上考虑过这种情况么
'[abc[bcd[def]ghi[jkl]]'
还是说这种嵌套的不在考虑范围内

commented
function normalize(str) {
  const arr = str.split(/[[\]]/).filter(Boolean) // str.split(']').join('').split('[').filter(Boolean)
  return arr.length > 1
    ? arr.reverse().reduce((acc, cur, index) => ({
        value: cur,
        children: index === 1 ? { value: acc } : acc
      }))
    : { value: str }
}
function parse(str){
            const list = str.split(/(\[|\])/).filter(val =>val);
            let result = null;
            let temp = null;
            for(let i = 0, len = list.length; i< len; i++){
                const value = list[i];
                if(value == ']'){
                    break;
                }
                
                switch(value){
                    case '[':
                      if(result){
                          result = (result.children || (result.children = {}));
                      }else{
                          result = temp =  {};
                      }
                      break;
                      default: 
                        if(result){
                            result.value = value;
                        }else{
                            result = temp =  { value: value};
                        }
                        break;
                }
                
            }
            return temp;

        }
    function normalize(str) {
		let res = str.match(/\[(.+?)\]/);
		let p = res ? res[1] : str;
		let arr = p.split('[');
		var obj = {};
		var cur = obj;
		for (let i = 0; i < arr.length; i++) {
			if (i > 0) {
				cur.children = {value: arr[i]}
				cur = cur.children
			} else {
				cur.value = arr[i];
			}
		}
		return obj;
	}
function normalize(str) {
	let arr = str.split(/[\[\]\s]+/).filter(Boolean);
	let result = {};
	arr.reduce((pre, next) => (pre.children = { value: next }), result);
	return result.children;
}
commented
// 字符串仅由小写字母和 [] 组成,且字符串不会包含多余的空格。
// 示例一: 'abc' --> {value: 'abc'}
// 示例二:'[abc[bcd[def]]]' --> {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}

function format(str) {
    const stack = []
    let i = 0
    let result

    while (i < str.length) {
        switch (str[i]) {
            case "[":
                i++
                break;
            case "]":
                i++
                stack.pop()
                break;
            default:
                let val = str[i];
                i++

                while (!['[', ']'].includes(str[i]) && i < str.length) {
                    val += str[i];
                    i++
                }

                const node = { val };

                if (!stack.length) {
                    result = node
                }

                if (stack.length && stack.length - 1 < stack.length) {
                  stack[stack.length - 1].children =
                    stack[stack.length - 1].children || [];
                  stack[stack.length - 1].children.push(node);
                }

                stack.push(node);
        }
    }

    return result;
}

console.log("format", format("[abc[bcd[def]]]"));
console.log("format", format("[abc[bcd[def][1]][2]]"));
function normalize(str) {
      let obj = {};
      const vals = str.split(/[\[\]]/g).filter(Boolean);
      for (let i = vals.length - 1; i >= 0; i--) {
        !obj.value ? obj.value = vals[i] : obj = {
          value: vals[i],
          children: {
            ...obj
          }
        }
      }
      return obj
    }

reduce 搞定

function normalize(input) {
  const arr = input.replace(/\[|\]/g, ',').split(',').filter(itm => itm);
  let result = {};
  arr.reduce((obj, cur, idx) => {
    const temp = {};
    temp.value = cur;
    if (obj.children) obj.children = temp;
    if (idx !== arr.length - 1) temp.children = {};
    if (idx === 0) result = temp;
    return temp;
  }, result);
  return result;
}

console.log(normalize('abc'));
console.log(normalize('[abc[bcd[def]]]'));

用栈实现

function normalize(str) {
  const stack = [];
  let res = {};

  for (let c of str) {
    if (c === ']') {
      let value = [];
      let top = stack.pop();

      while (top !== '[') {
        value.unshift(top);
        top = stack.pop();
      }

      if (res.value) {
        res.children = { ...res };
      }
      res.value = value.join('');
      continue;
    }
    stack.push(c);
  }

  if (stack.length) {
    res.value = stack.join('');
  }

  return res;
}
commented

es6实现

function normalize(str) {
  let obj = {};
  let arr = str.split(/[\[\]]/g).filter(Boolean);
  arr.reduce((acc, cur) => {
    while (acc.children) {
      acc = acc.children;
    }
    acc.value = cur;
    acc.children = {};
    return acc;
  }, obj);
  return obj;
}

es5实现

function findChild(obj) {
  return obj.children ? findChild(obj.children) : obj;
}

function normalize2(str) {
  let arr = str.split(/[\[\]]/g).filter(Boolean);
  let res = {};
  while ((item = arr.shift())) {
    let obj = findChild(res);
    obj.value = item;
    obj.children = {};
  }
  return res;
}

上面同学的测试用例

let arr = [
  "[a[b[c]]]",
  "",
  "asd",
  "[aads",
  "asdasd]",
  "[asddd]",
  "[a[d]",
  "[s[]]",
];
arr.reduce((acc, c) => ((acc[c] = normalize(c)), acc), {});

reverse 倒著回來

function normalize(str) {
  const arr = str.replaceAll(']', '').split('[').filter(Boolean)
  const res = arr.reverse().reduce((obj, value, idx, a) => {
    if (idx > 0) {
      obj.children = {
        ...obj
      }
    }
    obj.value = value
    return obj
  }, {})
  return res
}

const str = '[abc[bcd[def]]]'
console.log(normalize(str))
function parse(str) {
    let s = str
    const tokens = [
        /^\[/,
        /^\w+/,
        /^\]/
    ]
    let res = {}
    let point = undefined
    let parentMap = new Map()
    while (s) {
        for (let i = 0, l = tokens.length; i < l; i++) {
            let token = s.match(tokens[i])
            if (token) {
                s = s.replace(tokens[i], '')
                switch (i) {
                    case 0:
                        if (!point) {
                            point = res
                            parentMap.set(point, undefined)
                        } else {
                            point.children = {}
                            parentMap.set(point.children, point)
                            point = point.children
                        }
                        break
                    case 1:
                        point.value = token[0]
                        break
                    case 2:
                        point = parentMap.get(point)
                        break
                }
            }
        }
    }
    return res
}
commented
const normalize = (str = '') => {
    const result = {};
    const array = str.split(/[\[\]]/g).filter(Boolean);
    const { length } = array;

    array.reduce((previousValue, currentValue, currentIndex) => {
      previousValue.value = currentValue;
      if(currentIndex !== length - 1) return (previousValue.children = {})
    }, result);
    
    return result;
}
commented
/**
 *
 * @param {string} str
 */
const Normalize = (str) => {
  const obj = Object.create(null);
  const arr = str.match(/\w+/g); //[ 'abc', 'bcd', 'def' ]
  let index = 0;

  (function createObj(obj) {
    obj.value = arr[index++];
    if (index === arr.length) return;
    obj.children = {};
    return createObj(obj.children);
  })(obj);

  console.log(JSON.stringify(obj, undefined, '    '));
  return obj;
};

const Normalize = (str) => {
  const arr = str.match(/\w+/g); //[ 'abc', 'bcd', 'def' ]
  const obj = Object.create(null);

  arr.reduce((pre, current, index) => {
    pre['value'] = current;
    if (index == arr.length - 1) return null;
    pre['children'] = {};
    return pre['children'];
  }, obj);

  return obj;
};

console.log(Normalize('[abc[bcd[def]]]'));
function normalize(str) {
    const list = str.split(/[\[\]]/).filter(Boolean); // ['abc','bcd','def']
    const res = {};
    let temp = {};

    for (let i = 0; i < list.length; i++) {
        const item = list[i];
        if (i === 0) {
            res.value = item;
            temp = res;
        } else {
            temp.children = {
                value: item
            };
            temp = temp.children;
        }
    }
    return res;
}
let arr = '[abc[bcd[def[hij]]]]'.match(/\w+/g)
//['abc', 'bcd', 'def']
let obj = {};

let res = {
    val: arr.pop()
};
while (arr.length > 0) {
    res = {
        val: arr.pop(),
        child: res
    };
}
const normalize = (v) => {
    if (v[0] != '[') return { value: v };
    const jsonStr = v.replace(/\[([^\[\]]+)/g, (_, value) => (`"value": "${value}","children":{`))
        .replaceAll(']', '}')
        .replace(',"children":{}', '');
    return JSON.parse(`{${jsonStr}}`);
};
const normalize = (str) => {
    const root = {};
    const holdMap = [];
    let hold = root;
    let floor = 0;
    let subVal = '';
    const setVal = () => {
        if (!subVal) return;
        hold.value = subVal;
        subVal = '';
    };
    for (var c of str) {
        if (c === '[') {
            setVal();
            holdMap[++floor] = hold;
            hold.children = {};
            hold = hold.children;
        } else if (c === ']') {
            setVal();
            hold = holdMap[--floor];
        } else {
            subVal += c;
        }
    }
    setVal();
    return root.value ? root : (root.children || {});
};
commented
 const normalize2 = (str) => {
    let arr = str.split(/[\[\]]/g).filter(Boolean)
    let len = arr.length - 1
    var result = {}
//递归的形式
    let fun = (index, obj) => {
      for (let i = index; i < arr.length; i++) {
        obj.value = arr[index]
        if (i !== len) {
          obj.children = {}
          fun(index + 1, obj.children)
        }
      }
    }
    fun(0, result)
  }
function normalize(str) {
  let temp = []
  let valStart = false
  let nowVal = ''
  let obj = {}
  let resKey = ''
  for (let i = 0; i < str.length; i++) {
    if (str[i] == '[') {
      valStart = true
      if (nowVal !== '') {
        obj[nowVal] = {value: nowVal}
        if (temp.length > 0) {
          if (!obj[temp[temp.length - 1]].children) {
            obj[temp[temp.length - 1]].children = []
          }
          obj[temp[temp.length - 1]].children.push(obj[nowVal])
        }
        temp.push(nowVal)
      }
      nowVal = ''
    } else if (str[i] == ']') {
      valStart = false
      if (nowVal !== '') {
        obj[nowVal] = {value: nowVal}
        if (temp.length > 0) {
          if (!obj[temp[temp.length - 1]].children) {
            obj[temp[temp.length - 1]].children = []
          }
          obj[temp[temp.length - 1]].children.push(obj[nowVal])
        }
        temp.push(nowVal)
      }
      if (temp.length == 1) {
        resKey = temp[0]
      }
      temp.pop()
      nowVal = ''
    } else if (valStart) {
      nowVal += str[i]
    }
  }
  return obj[resKey]
}

看起来大部分题解都只能支持逐级的链式结构,以数组方式处理字符串必然会丢失数据的结构化。这样处理数据导致重要参数丢失显然是不够健壮

let str = '[abc[xx[yy][zz]][cc[dd]]]'
console.log(str)
console.log(normalize(str))

可以以栈的方式存储上一级对象名称并根据对象的堆特性,来实现将字符串处理为简单对象的能力。

function nomalize(str) {
    const props = str.match(/\w+/g);
    if (props.length === 1) { 
        return { value: props[0] };
    }
    
    return props.reduceRight((children, parent) => {
        if (typeof children === 'string') {
            children = { value: children };
        }
        
        return { value: parent, children };
    });
}
console.log(JSON.stringify(nomalize('abc')));
console.log(JSON.stringify(nomalize('[abc[bcd[def]]]')));