在Javascript中从对象中删除空白属性

515

如何在JavaScript对象中删除所有值为undefinednull的属性?

(该问题类似于此问题针对数组的解决方案)


27
强烈建议大家忽略排名靠前的版本,转而使用ES6/ES7版本,链接在这里:https://dev59.com/JnVC5IYBdhLWcg3wdw65#38340730 - Evan Carroll
5
ES6中不改变对象的一行代码也在这里:https://dev59.com/JnVC5IYBdhLWcg3wdw65#57625661 - chickens
ES6 -> JSON.parse(JSON.stringify(obj)) 的意思是将一个对象进行深拷贝。 - STEEL
2
小心!这两个都不能用于数组。 - Martin D
请查看链接,您不仅可以清除null或undefined,甚至可以定义自定义值以删除。 https://dev59.com/JnVC5IYBdhLWcg3wdw65#71968391 - ABHIJEET KHIRE
显示剩余3条评论
55个回答

907

ES10/ES2019 示例

一个简单的一行代码 (返回一个新对象)。

let o = Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));

与上述相同,但写成一个函数。

function removeEmpty(obj) {
  return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
}

这个函数使用递归来删除嵌套对象中的元素。

function removeEmpty(obj) {
  return Object.fromEntries(
    Object.entries(obj)
      .filter(([_, v]) => v != null)
      .map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v])
  );
}

ES6/ES2015 示例

一个简单的一行代码。警告:这会改变给定对象而不是返回新对象。

Object.keys(obj).forEach((k) => obj[k] == null && delete obj[k]);

一个声明(不会改变给定的对象)。

let o = Object.keys(obj)
  .filter((k) => obj[k] != null)
  .reduce((a, k) => ({ ...a, [k]: obj[k] }), {});

同上,但作为一个函数编写。

function removeEmpty(obj) {
  return Object.entries(obj)
    .filter(([_, v]) => v != null)
    .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
}

这个函数使用递归来从嵌套对象中删除项目。

function removeEmpty(obj) {
  return Object.entries(obj)
    .filter(([_, v]) => v != null)
    .reduce(
      (acc, [k, v]) => ({ ...acc, [k]: v === Object(v) ? removeEmpty(v) : v }),
      {}
    );
}

与上面的函数相同,但以命令式(非函数式)风格编写。

function removeEmpty(obj) {
  const newObj = {};
  Object.entries(obj).forEach(([k, v]) => {
    if (v === Object(v)) {
      newObj[k] = removeEmpty(v);
    } else if (v != null) {
      newObj[k] = obj[k];
    }
  });
  return newObj;
}

ES5/ES2009示例

在旧的日子里,事情更加冗长。

这是一个使用函数式风格编写的非递归版本。

function removeEmpty(obj) {
  return Object.keys(obj)
    .filter(function (k) {
      return obj[k] != null;
    })
    .reduce(function (acc, k) {
      acc[k] = obj[k];
      return acc;
    }, {});
}

这是一个非递归版本,采用命令式的风格编写。

function removeEmpty(obj) {
  const newObj = {};
  Object.keys(obj).forEach(function (k) {
    if (obj[k] && typeof obj[k] === "object") {
      newObj[k] = removeEmpty(obj[k]);
    } else if (obj[k] != null) {
      newObj[k] = obj[k];
    }
  });
  return newObj;
}

并且以函数式编程风格编写的递归版本。

function removeEmpty(obj) {
  return Object.keys(obj)
    .filter(function (k) {
      return obj[k] != null;
    })
    .reduce(function (acc, k) {
      acc[k] = typeof obj[k] === "object" ? removeEmpty(obj[k]) : obj[k];
      return acc;
    }, {});
}

3
@AugustinRiedinger 当我需要在换行和缩写之间做出决定时,有时如果我认为缩写是较小的罪恶,我会选择使用缩写。第5段代码并不难理解,它是一个函数,用于从一个对象中删除空的“键”,因此“o”和“k”是显而易见的。但我猜这是个人口味问题。 - Rotareti
3
首先版本是 ES5 风格的:Object.keys(myObj).forEach(function (key) {(myObj[key] == null) && delete myObj[key]}); - Neurotransmitter
2
一行代码,无函数: Object.entries(myObj).reduce((acc, [key, val]) => { if (val) acc[key] = val; return acc; }, {}) - Paul Slm
9
既然我们试图做到全面,那么看到一个不可变的解决方案可能会很好。这些方案会改变源对象并欺骗性地返回该对象,实际上这是不必要的,因为对象已经被改变了。初学者会捕获返回的对象值,并想知道为什么他们的源对象也被修改了。 - Mike McLin
12
无法处理数组(Object.keys 将返回数组元素的位置数字作为键)。可能有其他人遇到过这个问题,但我在测试5时发现了这个问题。 - Eelco
显示剩余16条评论

282

您可以循环遍历对象:

var test = {
  test1: null,
  test2: 'somestring',
  test3: 3,
}

function clean(obj) {
  for (var propName in obj) {
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];
    }
  }
  return obj
}

console.log(test);
console.log(clean(test));

如果您担心这个属性删除不会沿着对象的原型链运行,您还可以:

function clean(obj) {
  var propNames = Object.getOwnPropertyNames(obj);
  for (var i = 0; i < propNames.length; i++) {
    var propName = propNames[i];
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];
    }
  }
}

关于 null 和 undefined 的一些注记:

test.test1 === null; // true
test.test1 == null; // true

test.notaprop === null; // false
test.notaprop == null; // true

test.notaprop === undefined; // true
test.notaprop == undefined; // true

2
添加了一个快速修正。如果此代码片段在函数中使用,未声明的“i”变量将泄漏到外部作用域。 - Eric Nguyen
9
你可以将 (test[i]===null || test[i]===undefined) 简化为 (test[i]==null),这样更易懂,同时不改变原意。 - jaf0
嗨,@EricNguyen,与C和其他几种语言不同,JavaScript对于变量没有块级作用域(仅函数作用域),因此,在for块之后,变量_i_将始终泄漏到作用域中。 - Gerardo Lima
1
@GerardoLima,是的。我有点假设这将全部包装在一个函数中。我的意思是(假设这都包装在一个函数中),你需要变量声明,否则_i_会泄漏到函数范围之外。 - Eric Nguyen
这也将循环遍历原始对象的原型 - 在大多数情况下,这是不希望的。https://dev59.com/pXNA5IYBdhLWcg3wh-cC#2869372 - Rotareti
显示剩余3条评论

220

ES6+的最短单行代码

过滤所有假值(""0falsenullundefined

Object.entries(obj).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})

过滤nullundefined的值:

Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {})

只筛选null

Object.entries(obj).reduce((a,[k,v]) => (v === null ? a : (a[k]=v, a)), {})

仅筛选undefined

Object.entries(obj).reduce((a,[k,v]) => (v === undefined ? a : (a[k]=v, a)), {})

递归解决方案:过滤掉nullundefined

针对对象:

const cleanEmpty = obj => Object.entries(obj)
        .map(([k,v])=>[k,v && typeof v === "object" ? cleanEmpty(v) : v])
        .reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});

对于对象和数组:

const cleanEmpty = obj => {
  if (Array.isArray(obj)) { 
    return obj
        .map(v => (v && typeof v === 'object') ? cleanEmpty(v) : v)
        .filter(v => !(v == null)); 
  } else { 
    return Object.entries(obj)
        .map(([k, v]) => [k, v && typeof v === 'object' ? cleanEmpty(v) : v])
        .reduce((a, [k, v]) => (v == null ? a : (a[k]=v, a)), {});
  } 
}

21
这应该是唯一的答案!每个片段都将生成一个新对象,旧对象不会被改变。这更可取!需要注意的是,如果只使用 v == null,则会检查是否为 undefinednull - Megajin
1
@GOXR3PLUS 你是什么意思?从数组中删除 null 值就像这样简单:arr = arr.filter(v=>v!==null) - Superole
2
a,[k,v] => 累加器,键,值 - chickens
太棒了!reduce、三元运算符、逗号操作符、赋值表达式和单字母变量名都在同一语句中。 - ruohola
1
请您能否提供一下关于对象和数组递归解决方案中具体发生了什么的解释? - Nima
显示剩余6条评论

126

如果你正在使用lodash或underscore.js,这里有一个简单的解决方案:

var obj = {name: 'John', age: null};

var compacted = _.pickBy(obj);

这将仅适用于lodash 4,如果使用lodash 4之前的版本或underscore.js,请使用 _.pick(obj,_.identity);


1
太棒了!谢谢!顺便说一下,对我来说不明显的是你也可以这样使用它:foo().then(_.pickBy); // 过滤掉空结果 - Maciej Gurban
38
请注意,如果对象包含诸如0或空字符串之类的假值,则这将无法产生预期的结果。此时最好使用_.omit(obj, _.isUndefined) - JHH
14
_.isUndefined并不会忽略null,使用_.omitBy(obj, _.isNil)可以同时忽略undefinednull - Lukasz Wiktor
@LukaszWiktor 正确,问题确实要求未定义或空值。 - JHH

41

如果有人需要Owen和Eric答案的递归版本,这里是:

/**
 * Delete all null (or undefined) properties from an object.
 * Set 'recurse' to true if you also want to delete properties in nested objects.
 */
function delete_null_properties(test, recurse) {
    for (var i in test) {
        if (test[i] === null) {
            delete test[i];
        } else if (recurse && typeof test[i] === 'object') {
            delete_null_properties(test[i], recurse);
        }
    }
}

在 for 循环开始后,您应该使用 if(test.hasOwnProperty(i)) { ... } 检查对象 hasOwnProperty - Augie Gardner
@AugieGardner 我很好奇你为什么想要检查这个 - 如果你愿意,请解释一下。 (这不会防止检查继承的属性吗?) - Wumms

38

JSON.stringify会删除未定义的键。

removeUndefined = function(json){
  return JSON.parse(JSON.stringify(json))
}

这对于一个深层对象对我来说不起作用,但是Wumm在上面的答案有效。 - Suman
1
如果您需要将 null 视为 undefined,请使用替换函数,有关更多信息,请参见此答案:https://dev59.com/JnVC5IYBdhLWcg3wdw65#24190282 - Hooman Askari
2
请注意,此操作不会移除 null 值。尝试以下代码: let a = { b: 1, c: 0, d: false, e: null, f: undefined, g: [], h: {} },并执行 console.log(removeUndefined(a))。本问题是关于 undefinednull 值的。 - mayid
1
JSON方法是用于JSON字符串的,不应该在没有适当解析和检查的情况下用于JavaScript对象。 - Maciej Kwas
@MaciejKwas 这有什么区别吗?除了 JSON 要求键必须用双引号括起来之外,它们不都是一样的吗? - Lint
1
@Lint 是的,事实上它确实有很大区别。除了你可能会简单地丢失/不匹配你的数据(JSON不能正确处理日期、符号、类型化数组等,在可能的情况下调用toString()),你还可能遇到具有循环依赖性的 js 对象,在这种情况下,请自行查看JSON.stringify的行为。 - Maciej Kwas

21
您可以结合使用 JSON.stringify 、其 replacer 参数和 JSON.parse 将其转换回对象。使用此方法还意味着替换将适用于嵌套对象中的所有嵌套键。 示例对象
var exampleObject = {
  string: 'value',
  emptyString: '',
  integer: 0,
  nullValue: null,
  array: [1, 2, 3],
  object: {
    string: 'value',
    emptyString: '',
    integer: 0,
    nullValue: null,
    array: [1, 2, 3]
  },
  arrayOfObjects: [
    {
      string: 'value',
      emptyString: '',
      integer: 0,
      nullValue: null,
      array: [1, 2, 3]
    },
    {
      string: 'value',
      emptyString: '',
      integer: 0,
      nullValue: null,
      array: [1, 2, 3]
    }
  ]
};

替换函数

function replaceUndefinedOrNull(key, value) {
  if (value === null || value === undefined) {
    return undefined;
  }

  return value;
}

清洁物品

exampleObject = JSON.stringify(exampleObject, replaceUndefinedOrNull);
exampleObject = JSON.parse(exampleObject);

CodePen示例


1
对于那些使用 TypeScript 或更新版本的 Node.js/JavaScript 的用户,这是一个更短的版本。const clean = (object: object) => JSON.parse(JSON.stringify(object, (_, value) => value ?? undefined)); - Alexis Tyler
哇,这是迄今为止最好的。谢谢,@AlexMueller - Vixson
这对我有用,非常感谢。 - undefined

15

这是一个最简单的 Lodash 解决方案,用于返回一个过滤掉 nullundefined 值的对象。

_.omitBy(obj, _.isNil)


这是目前最干净的解决方案! - Jee Mok

13

使用json.stringify的replacer参数可以在一行中进行递归删除。

const removeEmptyValues = obj => (
  JSON.parse(JSON.stringify(obj, (k,v) => v ?? undefined))
)

使用方法:

removeEmptyValues({a:{x:1,y:null,z:undefined}}) // Returns {a:{x:1}}

正如 Emmanuel 在评论中提到的那样,如果您的数据结构仅包含可以放入 JSON 格式的数据类型(如字符串、数字、列表等),则此技术才有效。

(已更新答案以使用新的 空值合并运算符。根据浏览器支持的需求,您可能要使用此函数代替:(k,v) => v!=null ? v : undefined


4
这将把日期对象转换为字符串,并将NaN转换为null,但不会将其删除。 - Emmanuel N K

12

您可能正在寻找delete关键字。

var obj = { };
obj.theProperty = 1;
delete obj.theProperty;

5
他正在做的事情如上所述,这也仍然未定义对象中的内容。 - Josh Bedo
请查看链接,您不仅可以清除null或undefined,甚至可以定义自定义值以删除。 https://dev59.com/JnVC5IYBdhLWcg3wdw65#71968391 - ABHIJEET KHIRE

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