繁体   English   中英

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

[英]Filter out / remove elements matching criteria from the end of array

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

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

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' ]
[]

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

  • 不会改变它的输入
  • 不会不必要地计算输入的reverse
  • 不会使用findfindIndex不必要地搜索输入
  • 不会多次迭代输入
  • 不需要Array.prototype.reduceArray.prototype.reduceRight
  • 接受任何 可迭代的输入 - 不仅仅是数组

 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. 适当的尾调用
  2. 功能样式使用纯表达式

恢复到命令式样式,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' ] 

我使用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(); } 

您可以使用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(['', '', '', ''])); 

另一种方法是使用函数reduceRightSpread 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; } 

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

这是一个例子:

 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(['', '', '', ''])); 

有没有办法不需要所有这些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的答案向你展示了一个漂亮的功能解决方案,它与报价中的标签和关注点相匹配。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM