将嵌套数组简化为格式化的字符串

时间:2018-11-21 23:10:29

标签: javascript node.js

我想减少这个嵌套数组:

const list = ['Map<%s,%s>', ['string', 'Map<%s,%s>', ['string', 'boolean']]];

该列表将变为:

'Map<string,Map<string,boolean>>'

这是一个开始,但是回想起来确实让我感到困惑:

const util = require('util');

const reduceToString = function(l){
  return l.reduceRight((a,b) => {
    if(Array.isArray(a)){
      return reduceToString(a);
    }

    return util.format(b, a);

  });
};


console.log(reduce(list));

为了更好地理解它是如何工作的,请输入以下内容:

const list = ['Map<%s,%s,%s>', ['string', 'Map<%s,%s>', ['string', 'boolean'], 'number']];

应产生:

'Map<string,Map<string,boolean>,number>'

一般规则是:字符串右边的任何数组都应内插到字符串中,reduceToString函数应始终返回字符串。

3 个答案:

答案 0 :(得分:4)

一种方法可以是“减少”作为数组的任何元素(下降到最深的层次),然后在不断从堆栈中返回“下一个元素”是数组的任何元素时进行替换: / p>

const list = ['Map<%s,%s,%s>', ['string', 'Map<%s,%s>', ['string', 'boolean'], 'Number']];

function merge(list) {
  function reduce(arr) {
    arr = arr.map(e => Array.isArray(e) ? reduce(e) : e);
    return arr
      .map((e, i) => Array.isArray(arr[i + 1])
        ? arr[i + 1].reduce((a, c) => a.replace('%s', c), e)
        : e)
      .filter(e => !Array.isArray(e));
  }
  return reduce(list)[0];
}


console.log(merge(list));

答案 1 :(得分:3)

您可以使用replace()进行递归操作:

const list = ['Map<%s,%s,%s>', ['string', 'Map<%s,%s>', ['string', 'boolean'], 'Number']];

function replaceMatch(arr, i=0){
    let str = arr[0]
    return str.replace(/%s/g, () => {
        let next = arr[1]
        if (Array.isArray(next[i+1])) {
            i+=2
            return replaceMatch(next.slice(i-2))       
        }
        else return next[i++]
    })
}
str = replaceMatch(list)
console.log(str)

这是一个更长的但可能更易于阅读的递归版本。它返回一个数组而不是字符串,以允许非映射最终出现在结果中(例如,第一个和最后一个元素。如果映射的插槽比数组值多,它将添加一个?,并忽略多余的数组没有相应的%s的值:

const list = ["Name", 'Map<%s,%s,%s>', ['string', 'Map<%s,%s,%s>', ['string', 'boolean'], 'Number'], 'Set<%s,%s>', ['val_1', 'val_2']];

function replaceArr(str, arr){ // helper function for replace
    let i = 0
    return str.replace(/\%s/g, () => arr[i++] || '?')
}

function replaceMatch(arr){
  let res = []
  for (let i = 0; i < arr.length ; i++){
      if (Array.isArray(arr[i])) continue
      res.push(Array.isArray(arr[i+1])
          ? replaceArr(arr[i], replaceMatch(arr[i+1]))
          : arr[i]
        )
  }
  return res
}
str = replaceMatch(list)
console.log(str)

答案 2 :(得分:2)

这比我想象的要难得多。该解决方案返回一个数组,并且比其他解决方案和OP最初要求的更为通用。由于它可以连续处理两个字符串。

const util = require('util');
const list = ['Array','Map<%s,%s, %s>', ['xxx','Map<%s,%s>', ['string', 'boolean'], 'number']];

const reduce = function(list){
  return list.slice(1).reduce((a,b) => {

    if(Array.isArray(b)){
      const pop = a.pop();
      const format = util.format(pop,...reduce(b));
      return a.concat(format);
    }

    return (a.push(b), a);  // comma operator

  },
    [list[0]]
  );
};


console.log(reduce(list));

以上内容将产生:

[ 'Array', 'Map<xxx,Map<string,boolean>, number>' ]