将一些数组项合并

3

我对函数式编程比较陌生,我想要做以下的操作:假设我有一个值数组['a','b-','c'],我希望所有以'-'结尾的元素都与后面的元素进行合并,形成['a','b-c']

当然,我可以通过使用for循环来实现:

var test = ['a', 'b-', 'c'], result = [];

for (var i=0;i<test.length;i++) {
  var curr = test[i];
  if (curr.endsWith('-')) {
    curr += test[i+1];
    i++;
  }
  result.push(curr);
}

但是如何在不使用for循环的情况下完成这个操作呢?


2
我猜我会使用reduce,但是for循环有什么问题呢(尽管我认为你的代码存在一些边界情况问题,比如最后一个元素有一个最终破折号的情况)。 - user663031
6个回答

5
说实话,你编写的方式可能是完成此操作最有效的方式。
但是,这里有另一种选择:

var test = ['a', 'b-', 'c'],
    result = test.join().replace(/-,/g, '').split(',');

console.log(result);

这将所有元素连接成一个字符串:'a,b-,c',删除'-,'的所有出现 >'a,bc',然后将字符串拆分回所需的结果数组中,可以在输出中看到。可以通过更改join / split中使用的分隔符来使其更加安全。
var test = ['a', 'b-', 'c'],
    separator = '||',
    result = test.join(separator)
        .replace(new RegExp('-' + separator, 'g'), '')
        .split(separator);

如果有多个带破折号的项目,或者元素包含破折号后跟逗号等情况,这种方法就不起作用了。 - user663031
现在它可以了 ;-) - Cerbrus
如果数组中的某个值包含逗号,则此解决方案将无法正常工作。 - Willem de Wit
...replace(/-\|\|/g, '-')... - Thomas
看,这个答案有固有的弱点;无论你选择哪个分隔符,源数组中可能会包含该分隔符作为子字符串的元素。当然,可以稍微加强一下它,事先检查所有数组元素是否与其匹配,但这会使整个过程变得繁琐。不管怎样,我总体上喜欢这种技术 - 以及“超越常规”的方法,所以我给它一个+1。 - raina77ow
显示剩余2条评论

2

一种可能的方法(使用.reduce):

var arr = ['a', 'b-', 'c'];
var trans = arr.reduce(function(acc, cur) {
  if (acc.length && acc[acc.length - 1].slice(-1) === '-') {
    acc[acc.length - 1] += cur;
  }
  else {
    acc.push(cur);
  }
  return acc;
}, []);

建议:str[str.length - 1] === '-' - Cerbrus
哦,现在我明白了。acc是数组。 - Cerbrus
我喜欢这个答案,因为它没有使用闭包。 - Willem de Wit
那么使用闭包会得到什么答案呢? - Cerbrus

1

这也可以使用 Array.prototype.map 来实现:

var test = ['a', 'b-', 'c'];

var result = test.slice().map(function (x, i, a) {
    if (x.endsWith("-") && a[i+1]) {
        var r = x + a[i+1]; // Join this and the next element in the array
        a.splice(i, 1); // Remove the next element from the array
        return r;
    }
    return x;
}).filter(function (x) {
    return typeof x !== 'undefined';
}); // Since the array comes back with a different length and some undefined elements, remove those. Thanks @Cerbrus for pointing this out

console.log(test, result, result.length); // ["a", "b-", "c"] ["a", "b-c"] 2

当您使用 map 时,result 将具有尾随的 undefined 元素:["a", "b-c", undefined × 1]。这意味着 result.length === 3。此外,test(即输入数组)将被修改。 - Cerbrus
不完全正确。result.length 仍然是 3。请注意,console.log(result) 不会记录数组末尾的未定义元素,但是尝试记录 length 或只需在控制台中输入 result - Cerbrus
使用 a.splice 会改变输入的数组。不要这样做! - Bergi
@Bergi: a 是输入数组的一个副本。(test.slice()) - Cerbrus
@Cerbrus:哦,没错。但这是可怕的代码 :-/ filter 不能处理假值,应该明确测试 undefined - Bergi
1
你可以使用.filter(Boolean)代替.filter(function (x) {return x;}); - Willem de Wit

0

这种方法适用于一行中有多个破折号元素,如果最后一个元素有破折号,则使用Array.forEach

var test = ['a', 'b-', 'c-'], result = [], next = "";

test.forEach(function(curr) {
  if (curr.endsWith('-')) {
    next += curr;
    if (curr == test[test.length-1]) {
      result.push(next);
    }
 }else {
   result.push(next + curr);
   next = "";
 }
});
document.write(result);

1
不要滥用 map 作为循环。如果你不想要结果,至少使用 forEach,这样就清楚了。 - Bergi

0

没有读完所有的答案,如果我重复了已经说过的话,请原谅。

函数式编程并不意味着总是有一个预定义的函数恰好做你想要的事情;或者一些函数的组合。
有时候,编写一个简单的短小的实用函数比滥用已经存在的函数更好。

那么像多个连字符值相邻,或者在列表末尾的一些“问题”怎么办?
这是我的实现:

function combineDashedStrings(arr){
    var result = [], pending = ""
    for (var i=0; i<arr.length; i++) {
        var curr = pending + arr[i];

        pending = curr.endsWith("-") && curr || "";                 //multiple concats
        //pending = !pending && curr.endsWith("-") && curr || "";   //single concat

        pending || result.push(curr);
    }
    //pending && result.push(curr); //add last item if it contains a dash
    return result
}

combineDashedStrings(['a', 'b-', 'c-', 'd', 'e-']);

随意切换注释行/选项


返回翻译后的文本:return result 而不是 return arr。在发布前测试您的代码。 - Cerbrus

0

另一个 map + filter 的例子。很可能比较慢,因为 filter 会添加一个额外的数组迭代,但是它可以像原始函数一样工作(当有多个连续的 - 时,这可能不是 OP 想要的结果)。

var test = ['a', 'b-', 'c-', 'd', 'e'], result = [];

result = test
 .map((curr, i, array) => (curr.endsWith('-') && array[i + 1] !== undefined) ? curr + array[i+1] : curr)
 .filter((curr, i, arr) => (i>0 && arr[i-1].length > 1 && curr.length === 1) ? false : true)

document.write(result);


那么,这个返回的结果是不正确的,与OP的尝试返回的结果相同? - Cerbrus
@Cerbrus 我不确定 OP 是想要 ['a', 'b-c-d'] 还是 ['a', 'b-c-' ,'c-d'] 作为 ['a', b-', 'c-', 'd'] 的结果。如果应该是第二种情况,那么上面的代码片段是一个起点。我建议使用 for 循环,因为这很可能是最有效的方法。 - nylki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接