在Javascript中从数组中删除空元素

1601

如何从JavaScript数组中删除空元素?

有没有简单直接的方法,还是必须手动循环并逐个删除?


27
如果你的问题能够明确指出“空元素”指的是什么,那将会很有帮助,因为这里的大部分答案(在我看来)错误地将其解释为“假值”元素。注意:var a = [,,]var a = [undefined, undefined]之间存在区别。前者是真正的空数组,而后者实际上有两个键,但值为undefined - Alnitak
虽然不是完全的答案,但我认为在数组中尽可能避免使用null/undefined是更好的实践。例如,如果你的null来自于使用map函数映射另一个数组时,对于某些元素返回null,那么在运行map之前尝试使用Array.filter过滤掉这些元素。这样可以使你的代码更易读/自我记录。显然,这并不适用于每种情况,但它可以应用于很多情况。 - Luke Redmore
51个回答

3
var data = [null, 1,2,3];
var r = data.filter(function(i){ return i != null; })

console.log(r) 

[1,2,3]


这是显然正确的做法,应该放在最上面! - Martin Andersson

3

去除所有空元素

如果一个数组中包含空对象、数组和字符串,以及其他空元素,我们可以使用以下代码将它们删除:

const arr = [ [], ['not', 'empty'], {}, { key: 'value' }, 0, 1, null, 2, "", "here", " ", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ]

let filtered = JSON.stringify(
  arr.filter((obj) => {
    return ![null, undefined, ''].includes(obj)
  }).filter((el) => {
    return typeof el != "object" || Object.keys(el).length > 0
  })
)

console.log(JSON.parse(filtered))

简单压缩(从数组中删除空元素)

使用 ES6:

const arr = [0, 1, null, 2, "", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ,]

let filtered = arr.filter((obj) => { return ![null, undefined].includes(obj) })

console.log(filtered)

使用纯JavaScript ->

var arr = [0, 1, null, 2, "", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ,]

var filtered = arr.filter(function (obj) { return ![null, undefined].includes(obj) })

console.log(filtered)


嘿,我只是想问一下,在 ES6 中是否有一种方法可以从二维/嵌套数组中删除 null 元素? - Mob_Abominator
嗨@Mob_Abominator,这肯定是可以做到的,请尝试通过访问嵌套数组来使用相同的逻辑。 - Zalom

3
您可以使用带有索引和 in 运算符的过滤器。

let a = [1,,2,,,3];
let b = a.filter((x,i)=> i in a);

console.log({a,b});


假设数组包含索引号,...? - Magne
在JS标准数组中索引是数字。JS对象可以包含除数字以外的其他键。JS数组也是对象,您可以在其中放置键值对,例如let a=[]; a.abc=1 - 但通常没有人这样做(这是相当奇特的 - 因为您可以使用例如对象{}而不是数组)。当您如此奇特地使用数组时,标准JS数组方法如mapfilter等将忽略这些键值对 - 我的解决方案基于filter并忽略它。很明显OP询问了标准JS数组(因为他没有提到这种奇特的用例)。 - Kamil Kiełczewski
是的,实际上我不是指那个,但还是谢谢你澄清了这一点。我大致浏览了 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in 的内容,混淆地认为 in 运算符返回数组中的值。因此,我认为它只有在数组中的值与想要过滤的索引号相同时才起作用。但我现在看到 in 是指数组的属性(如索引),而不是值。 - Magne
SO不允许我更改我的投票。:( 如果您进行小的编辑,那么我就可以更改我的投票。 - Magne
为什么这是必要的?.filter()将跳过数组中的空槽,因此a.filter((x,i)=> i in a);检查现有元素。而且,现有元素的索引存在于数组中是隐含的。因此,它简化为a.filter(() => true); 一些 其他 答案 在这里 已经展示了这种方法。 - VLAZ

3
我只是在以上“使用ES5的Array.filter()全局构造函数”高尔夫球干扰中加入了我的声音,但我建议使用Object而不是上面建议的String、Boolean或Number。
具体来说,ES5的filter()已经不会触发数组中的undefined元素;因此,一个普遍返回true的函数(返回filter()访问到的所有元素),必然只返回非undefined元素:
> [1,,5,6,772,5,24,5,'abc',function(){},1,5,,3].filter(function(){return true})
[1, 5, 6, 772, 5, 24, 5, 'abc', function (){}, 1, 5, 3]

不过,写出...(function(){return true;})比写...(Object)要长一些;而且在任何情况下,Object构造函数的返回值都会是某种对象。与上面建议使用的原始类型装箱构造函数不同,没有可能的对象值是falsey,因此在布尔设置中,Objectfunction(){return true}的简写。

> [1,,5,6,772,5,24,5,'abc',function(){},1,5,,3].filter(Object)
[1, 5, 6, 772, 5, 24, 5, 'abc', function (){}, 1, 5, 3]

1
注意:filter(String)和filter(Object)不能过滤空值或数字。因为构造函数也是一个函数,所以你可以把String传递给filter,例如someArray.filter(String);实际上等同于someArray.filter(function(x){ return String(x); });。如果你希望删除所有假值,可以使用someArray.filter(Boolean);来删除0、-0、NaN、false、''、null和undefined。 - robocat
1
不错的回答,尽管我想知道调用Object构造函数与使用return true方法相比的性能开销。@robocat提问者要求删除空元素,而不是null。 - Alnitak
我更喜欢最短、最清晰的解决方案,除了在紧密循环中。这只是我的个人偏好。 - ELLIOTTCABLE

2

以上答案并不适用于所有类型。以下解决方案将删除null、undefined、{} []NaN,并保留日期字符串,最好的是它甚至可以从嵌套对象中删除。

function removeNil(obj) {
    // recursively remove null and undefined from nested object too.
    return JSON.parse(JSON.stringify(obj), (k,v) => {
      if(v === null || v === '') return undefined;
      // convert date string to date.
      if (typeof v === "string" && /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/.test(v))
        return new Date(v);
      // remove empty array and object.
      if(typeof v === 'object' && !Object.keys(v).length) return undefined;
      return v;
    });
  }

function removeNil(obj) {
    // recursively remove null and undefined from nested object too.
    return JSON.parse(JSON.stringify(obj), (k,v) => {
      if(v === null || v === '') return undefined;
      // convert date string to date.
      if (typeof v === "string" && /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/.test(v))
        return new Date(v);
      // remove empty array and object.
      if(typeof v === 'object' && !Object.keys(v).length) return undefined;
      return v;
    });
  }
  
 const ob = {
  s: 'a',
  b: 43,
  countries: [ 'a', 'b', 'c' ],
  l: null,
  n: { ks: 'a', efe: null, ce: '' },
  d: new Date(),
  nan: NaN,
  k: undefined,
  emptyO: {},
  emptyArr: [],
 }
 
 const output = removeNil(ob);
 
 console.log(output);
 console.log('Tests: ', ob.countries.length, typeof(ob.d))


2

那么这个怎么样:

js> [1,2,,3,,3,,,0,,,4,,4,,5,,6,,,,].filter(String).join(',')
1,2,3,3,0,4,4,5,6

2
另一种方法是利用数组的长度属性:将非空项目打包到数组的“左侧”,然后减少长度。这是一个原地算法-不分配内存,对于垃圾收集器来说太糟糕了-它具有非常好的最佳/平均/最差情况行为。
与此处的其他解决方案相比,这个解决方案在Chrome上快2到50倍,在Firefox上快5到50倍,您可以在这里看到:http://jsperf.com/remove-null-items-from-array 下面的代码向数组添加了不可枚举的“removeNull”方法,该方法返回可用于Daisy-Chaining的“this”:
var removeNull = function() {
    var nullCount = 0           ;
    var length    = this.length ;
    for (var i=0, len=this.length; i<len; i++) { if (!this[i]) {nullCount++} }
    // no item is null
    if (!nullCount) { return this}
    // all items are null
    if (nullCount == length) { this.length = 0; return this }
    // mix of null // non-null
    var idest=0, isrc=length-1;
    length -= nullCount ;                
    while (true) {
         // find a non null (source) slot on the right
         while (!this[isrc])  { isrc--; nullCount--; } 
         if    (!nullCount) { break }       // break if found all null
         // find one null slot on the left (destination)
         while ( this[idest]) { idest++  }  
         // perform copy
         this[idest]=this[isrc];
         if (!(--nullCount)) {break}
         idest++;  isrc --; 
    }
    this.length=length; 
    return this;
};  

Object.defineProperty(Array.prototype, 'removeNull', 
                { value : removeNull, writable : true, configurable : true } ) ;

不错的回答,不过最好能够展示一些测试用例来演示它的实际应用! - Alnitak
2
这个答案非常有趣,但有点让我想起看着一台1945年建造的计算机,而我现在有一部智能手机:arr.filter(e => e) - agm1984
@agm1984,你的智能手机并不聪明。 - Hernán Eche
这可能取决于您对“聪明”的定义 - 例如动词,引起剧烈刺痛。由于您的评论,如果我将我的手机武器化,这与身体上的疼痛有关。 - agm1984

1
var data= { 
    myAction: function(array){
        return array.filter(function(el){
           return (el !== (undefined || null || ''));
        }).join(" ");
    }
}; 
var string = data.myAction(["I", "am","", "working", "", "on","", "nodejs", "" ]);
console.log(string);

输出:

我正在使用nodejs工作

它将从数组中删除空元素并显示其他元素。


我正在使用Node.js进行开发。它将从数组中删除空元素并显示其他元素。 - Jitendra virani
我改进了你的回答。请尽量让回答简单、清晰和易读 ;) - GGO

1

“误用” for ... in (object-member) 循环。 => 只有真值出现在循环体中。

// --- Example ----------
var field = [];

field[0] = 'One';
field[1] = 1;
field[3] = true;
field[5] = 43.68;
field[7] = 'theLastElement';
// --- Example ----------

var originalLength;

// Store the length of the array.
originalLength = field.length;

for (var i in field) {
  // Attach the truthy values upon the end of the array. 
  field.push(field[i]);
}

// Delete the original range within the array so that
// only the new elements are preserved.
field.splice(0, originalLength);

代码是正确的,但注释是错误的。使用 for ... in 的行为是从数组中删除未定义的键,但实际上您在此处没有其他代码来仅接受“真实”的值。 - Alnitak

1
这可能会对您有所帮助:https://lodash.com/docs/4.17.4#remove
var details = [
            {
                reference: 'ref-1',
                description: 'desc-1',
                price: 1
            }, {
                reference: '',
                description: '',
                price: ''
            }, {
                reference: 'ref-2',
                description: 'desc-2',
                price: 200
            }, {
                reference: 'ref-3',
                description: 'desc-3',
                price: 3
            }, {
                reference: '',
                description: '',
                price: ''
            }
        ];

        scope.removeEmptyDetails(details);
        expect(details.length).toEqual(3);

scope.removeEmptyDetails = function(details){
            _.remove(details, function(detail){
                return (_.isEmpty(detail.reference) && _.isEmpty(detail.description) && _.isEmpty(detail.price));
            });
        };

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