从JS对象中删除所有空字符串,空对象和空数组 [英] Remove all empty strings, empty objects, and empty arrays from JS object

查看:144
本文介绍了从JS对象中删除所有空字符串,空对象和空数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的对象

{
   "name":"fff",
   "onlineConsultation":false,
  
   "image":"",
   "primaryLocation":{
      "locationName":"ggg",
      "street":"",
   
   },
  
   "billingAndInsurance":[
      
   ],
  
   "categories":[
      ""
   ],
   "concernsTreated":[
      ""
   ],
   "education":[
      {
         "nameOfInstitution":"ffff",
         "description":"fff",
        
      }
   ],
   "experience":[
      {
         "from":"",
         "current":"",
        
      }
   ],
}

从中递归地删除所有空对象和空数组的算法是什么?这是我的代码:

What is the algorithm to recursively remove all empty objects, and empty arrays from this? this is my code:

function rm(obj) {
  for (let k in obj) {
    const s = JSON.stringify(obj[k]);

    if (s === '""' || s === "[]" || s === "{}") {
      delete obj[k];
    }
    if (Array.isArray(obj[k])) {
      obj[k] = obj[k].filter((x) => {
        const s = JSON.stringify(obj[x]);
        return s !== '""' && s !== "[]" && s !== "{}";
      });
      obj[k] = obj[k].map(x=>{
        return rm(x)
      })
      
    }
  }
  return obj
}

我尝试了多种算法,但是没有一个奏效.上面的那个应该更加完整一些.但是我已经用尽所有资源使它正常工作

I'v tried multiple algorithms, but none worked. the one above should work with a little more completeness. But I'v exhausted all my resources to make it work

推荐答案

关于保留有用功能的一件好事是,您通常可以很简单地解决新要求.使用我多年来编写的一些库函数,我能够编写以下版本:

One nice thing about keeping around helpful functions is that you can often solve for your new requirements pretty simply. Using some library functions I've written over the years, I was able to write this version:

const removeEmpties = (input) =>
  pathEntries (input)
    .filter (([k, v]) => v !== '')
    .reduce ((a, [k, v]) => assocPath (k, v, a), {})

这使用了我拥有的两个函数 pathEntries assocPath ,下面将给出它们的实现.给定您提供的输入后,它将返回以下内容:

This uses two function I had around, pathEntries and assocPath, and I'll give their implementations below. It returns the following when given the input you supplied:

{
    name: "fff",
    onlineConsultation: false,
    primaryLocation: {
        locationName: "ggg"
    },
    education: [
        {
            nameOfInstitution: "ffff",
            description: "fff"
        }
    ]
}

这将删除空字符串,不包含任何值的数组(在删除空字符串之后)和不包含非空值的对象.

This removes empty string, arrays with no values (after the empty strings are removed) and objects with no non-empty values.

我们首先调用 pathEntries (我在这里的其他答案中使用了它,包括一个相当新的版本.)这将收集到输入对象中所有叶节点的路径以及这些叶上的值.路径存储为字符串数组(用于对象)或数字数组(用于数组.),并与值一起嵌入到数组中.所以在那一步之后,我们得到类似

We begin by calling pathEntries (which I've used in other answers here, including a fairly recent one.) This collects paths to all the leaf nodes in the input object, along with the values at those leaves. The paths are stored as arrays of strings (for objects) or numbers (for arrays.) And they are embedded in an array with the value. So after that step we get something like

[
  [["name"], "fff"],
  [["onlineConsultation"], false],
  [["image"], ""],
  [["primaryLocation", "locationName"], "ggg"],
  [["primaryLocation", "street"], ""],
  [["categories", 0], ""], 
  [["concernsTreated", 0], ""], 
  [["education", 0, "nameOfInstitution"], "ffff"],
  [["education", 0, "description"],"fff"],
  [["experience", 0, "from"], ""],
  [["experience", 0, "current"], ""]
]

这看起来类似于对象的 Object.entries 结果,除了键不是属性名称而是整个路径之外.

This should looks something like the result of Object.entries for an object, except that the key is not a property name but an entire path.

接下来,我们进行过滤以删除任何带有空字符串值的内容,从而产生:

Next we filter to remove any with an empty string value, yielding:

[
  [["name"], "fff"],
  [["onlineConsultation"], false],
  [["primaryLocation", "locationName"], "ggg"],
  [["education", 0, "nameOfInstitution"], "ffff"],
  [["education", 0, "description"],"fff"],
]

然后通过减少对 assocPath 的调用(我已经使用了很多次的其他功能,包括在有趣的问题)和一个空对象,我们将这些叶节点放在正确的路径上水合一个完整的对象,并得到了我们正在寻找的答案. assocPath 是另一个函数 assoc 的扩展,该函数将属性名称与对象中的值不可变地关联.虽然它不是那么简单,但是由于对数组和对象的处理,您可以想到 assoc ,例如(name,val,obj)=>.({... obj,[name]:val}) assocPath 对对象路径(而不是属性名称)做了类似的操作.

Then by reducing calls to assocPath (another function I've used quite a few times, including in a very interesting question) over this list and an empty object, we hydrate a complete object with just these leaf nodes at their correct paths, and we get the answer we're seeking. assocPath is an extension of another function assoc, which immutably associates a property name with a value in an object. While it's not as simple as this, due to handling of arrays as well as objects, you can think of assoc like (name, val, obj) => ({...obj, [name]: val}) assocPath does something similar for object paths instead of property names.

关键是我为此只编写了一个新函数,否则就使用了我以前遇到的东西.

The point is that I wrote only one new function for this, and otherwise used things I had around.

通常,我更愿意为此编写一个递归函数,而我最近是这样的.但这并不容易扩展到这个问题,如果我理解正确,我们想在数组中排除空字符串,然后,如果该数组本身现在为空,则还要排除它.这种技术使这一点变得简单.在下面的实现中,我们将看到 pathEntries 依赖于递归函数,而 assocPath 本身就是递归的,所以我想仍然还有递归!

Often I would prefer to write a recursive function for this, and I did so recently for a similar problem. But that wasn't easily extensible to this issue, where, if I understand correctly, we want to exclude an empty string in an array, and then, if that array itself is now empty, to also exclude it. This technique makes that straightforward. In the implementation below we'll see that pathEntries depends upon a recursive function, and assocPath is itself recursive, so I guess there's still recursion going on!

我还应注意, pathEntries 中使用的 assocPath path 函数的灵感来自

I also should note that assocPath and the path function used in pathEntries are inspired by Ramda (disclaimer: I'm one of it's authors.) I built my first pass at this in the Ramda REPL and only after it was working did I port it to vanilla JS, using the versions of dependencies I've created for those previous questions. So even though there are a number of functions in the snippet below, it was quite quick to write.

const path = (ps = []) => (obj = {}) =>
  ps .reduce ((o, p) => (o || {}) [p], obj)

const assoc = (prop, val, obj) => 
  Number .isInteger (prop) && Array .isArray (obj)
    ? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
    : {...obj, [prop]: val}

const assocPath = ([p = undefined, ...ps], val, obj) => 
  p == undefined
    ? obj
    : ps.length == 0
      ? assoc(p, val, obj)
      : assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)

const getPaths = (obj) =>
  Object (obj) === obj
    ? Object .entries (obj) .flatMap (
        ([k, v]) => getPaths (v) .map (p => [Array.isArray(obj) ? Number(k) : k, ... p])
      )
    : [[]]

const pathEntries = (obj) => 
  getPaths (obj) .map (p => [p, path (p) (obj)])

const removeEmpties = (input) =>
  pathEntries (input)
    .filter (([k, v]) => v !== '')
    .reduce ((a, [k, v]) => assocPath (k, v, a), {})

const input = {name: "fff", onlineConsultation: false, image: "", primaryLocation: {locationName: "ggg", street:""}, billingAndInsurance: [], categories: [""], concernsTreated: [""], education: [{nameOfInstitution: "ffff", description: "fff"}], experience: [{from: "", current:""}]}

console .log(removeEmpties (input))

在某些时候,我可能会选择更进一步.我看到一个 hydrate 函数正在被拔出:

At some point, I may choose to go a little further. I see a hydrate function looking to be pulled out:

const hydrate = (entries) =>
  entries .reduce ((a, [k, v]) => assocPath2(k, v, a), {})

const removeEmpties = (input) =>
  hydrate (pathEntries (input) .filter (([k, v]) => v !== ''))

我还可以看到,这种书写方式更像Ramda风格:

And I can also see this being written more Ramda-style like this:

const hydrate = reduce ((a, [k, v]) => assocPath(k, v, a), {})

const removeEmpties = pipe (pathEntries, filter(valueNotEmpty), hydrate)

带有适当版本的 valuesNotEmpty .

但所有这些都是另一天的事情.

But all that is for another day.

这篇关于从JS对象中删除所有空字符串,空对象和空数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆