从数组末尾过滤/删除匹配条件的元素

时间:2018-03-25 21:54:49

标签: javascript functional-programming

我有一个字符串数组。有些元素是空的,即''(没有nullundefined或仅包含空格字符的字符串。我需要删除这些空元素,但只能从数组的(右)端删除。应保留非空元素之前的空元素。如果数组中只有空字符串,则应返回空数组。

以下是我到目前为止的代码。它有效,但我想知道 - 有没有一种方法不需要所有这些if?我可以不创建数组的内存副本吗?

function removeFromEnd(arr) {
    t = [...arr].reverse().findIndex(e => e !== '');

    if (t === 0) {
        return arr;
    } else if (t === -1) {
        return [];
    } else {
        return arr.slice(0, -1 * t);
    }
}

测试用例

console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));
[ 'a', 'b', '', 'c' ]
[ 'a' ]
[ '', '', '', '', 'c' ]
[]

6 个答案:

答案 0 :(得分:4)

这是removeFromEnd的尾递归实现。我们的实施:

  • 不会改变其输入
  • 不会不必要地计算输入的reverse
  • 不会使用findfindIndex
  • 进行不必要的搜索
  • 不会多次迭代输入
  • 不需要Array.prototype.reduceArray.prototype.reduceRight
  • 接受任何 iterable输入 - 不仅仅是阵列



const identity = x =>
  x

const Empty =
  Symbol ()

const removeFromEnd = ([ x = Empty, ...xs ], cont = identity) =>
  x === Empty
    ? cont ([])
    : removeFromEnd (xs, end =>
        end.length === 0 && x === ''
          ? cont ([])
          : cont ([ x, ...end ]))

console.log (removeFromEnd ([ 'a', 'b', '', 'c', '', '' ]))
// [ 'a', 'b', '', 'c' ]

console.log (removeFromEnd ([ 'a', '', '' ]))
// [ 'a' ]

console.log (removeFromEnd ([ '', '', '', '', 'c' ]))
// [ '', '', '', '', 'c' ]

console.log (removeFromEnd ([ '', '', '', '' ]))
// []




我认为removeFromEnd可以作为高阶函数进行改进,就像Array.prototype.filter

一样



const identity = x =>
  x

const Empty =
  Symbol ()

const removeFromEnd = (f = Boolean, [ x = Empty, ...xs ], cont = identity) =>
  x === Empty
    ? cont ([])
    : removeFromEnd (f, xs, end =>
        end.length === 0 && f (x)
          ? cont ([])
          : cont ([ x, ...end ]))

console.log (removeFromEnd (x => x === '', [ 'a', 'b', '', 'c', '', '', false, 0 ]))
// [ 'a', 'b', '', 'c', '', '', false, 0 ]

console.log (removeFromEnd (x => !x, [ 'a', 'b', '', 'c', '', '', false, 0 ]))
// [ 'a', 'b', '', 'c' ]




对于初学者来说,removeFromEnd表示没有幻想的ES6解构语法或箭头函数



const identity = function (x)
{
  return x
}

const removeFromEnd = function (f = Boolean, xs = [], i = 0, cont = identity)
{
  if (i > xs.length)
    return cont ([])
  else
    return removeFromEnd (f, xs, i + 1, function (end) {
      if (end.length === 0 && f (xs [i]))
        return cont ([])
      else
        return cont ([ xs [i] ].concat (end))
    })
}

const data =
  [ 'a', 'b', '', 'c', '', '', false, 0 ]

console.log (removeFromEnd (x => x === '', data))
// [ 'a', 'b', '', 'c', '', '', false, 0 ]

console.log (removeFromEnd (x => !x, data))
// [ 'a', 'b', '', 'c' ]




上面使用的延续传递方式对您来说可能看起来很陌生,但如果您想要写removeFromEnd,这是必不可少的:

  1. 使用适当的tail call
  2. 使用纯表达式的功能样式
  3. 恢复命令式样式,JavaScript允许我们编写没有持续复杂性的程序 - 但是,递归调用不再处于尾部位置

    我分享了这个版本的程序,因为大多数对功能性思维不熟悉的人来自于编写这样的程序。看到以不同风格表达的同一个程序,总能帮助我。也许它可以用同样的方式帮助你^^

    
    
    const removeFromEnd = function (f = Boolean, xs = [], i = 0)
    {
      if (i > xs.length)
        return []
      
      const end =
        removeFromEnd (f, xs, i + 1)
        
      if (end.length === 0 && f (xs [i]))
        return []
      
      return [ xs [i] ] .concat (end)
    }
    
    const data =
      [ 'a', 'b', '', 'c', '', '', false, 0 ]
    
    console.log (removeFromEnd (x => x === '', data))
    // [ 'a', 'b', '', 'c', '', '', false, 0 ]
    
    console.log (removeFromEnd (x => !x, data))
    // [ 'a', 'b', '', 'c' ]
    
    
    

答案 1 :(得分:1)

我使用reduce并且只添加一个元素,如果它不是空字符串或者数组中已经有条目

console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

function removeFromEnd(arr) {
  return arr.reduceRight((a, b) => {
    if (b !== '' || a.length) a.push(b);
    return a;
  }, []).reverse();
}

答案 2 :(得分:1)

您可以使用reduceRight执行此操作,并在找到非空字符串的第一个元素时更改一个值。

function removeFromEnd(arr) {
  return arr.reduceRight((r, e) => {
    if (e) r.match = true;
    if (r.match) r.arr.unshift(e)
    return r;
  }, {arr: []}).arr
}

console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

答案 3 :(得分:1)

另一种方法是使用 reduceRight 功能以及 Spread syntax



let removeFromEnd = (arr) => arr.reduceRight((a, b) => ((b !== '' || a.length) ? [b, ...a] : a), []);    

console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

.as-console-wrapper { max-height: 100% !important; top: 0; }




答案 4 :(得分:0)

您可以反转数组并删除元素,直到找到非空字符串。

以下是一个例子:

function removeFromEnd(arr){
   return arr.reverse().filter(function(v){
     if(v !== ""){
        this.stopRemoving = true;
     }
     return this.stopRemoving;
   }, {stopRemoving: false}).reverse();
}


console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

以下是使用经典循环的示例

function removeFromEnd(arr){
   for(var i = arr.length - 1; i >= 0; i--){
     if(arr[i] !== ""){
       break;
     }
     arr.pop();
   }
   return arr;
}


console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

答案 5 :(得分:0)

  

有没有一种方法不需要所有这些ifs?我可以不创建数组的内存副本吗?

可能不是很实用,但是非常直接的实现:

function removeFromEnd(arr) {
    let i = arr.length - 1;

    while (arr[i] === "") i--;

    return arr.slice(0, i + 1);
}

console.log(removeFromEnd(['a', 'b', '', 'c', '', '']));
console.log(removeFromEnd(['a', '', '']));
console.log(removeFromEnd(['', '', '', '', 'c']));
console.log(removeFromEnd(['', '', '', '']));

这里很难找到平衡......你标记了functional-programming,@ naomik的答案显示了一个漂亮的功能解决方案,它与报价中的标签和关注点相匹配。

如果您只是想简化和优化,我的片段可能就足够了......