【问题标题】:Filtering array of object using key in Javascript (node.js)使用 Javascript (node.js) 中的键过滤对象数组
【发布时间】:2020-06-23 10:01:54
【问题描述】:

请任何人都可以提供帮助,我想根据 node.js 中的键过滤数据,我已经尝试了大部分事情,但没有奏效。 下面是需要过滤的示例数据。我可以过滤键:值对,嵌套的 Json 对象,但过滤 json 对象的数组不起作用。 例如。变量数据 =

[{
    "createdBy":"Tom"
    "logistics" : 0,
    "paymentStatus" : "Paid",
    "orderAmount" : 5393.75,
    "details" : {
         "street": "S.S road",
         "postCOde": "440111",
    },
    "subOrders" : [
        {
        "name" : "sub Product1",
        "mrp" : 12,
        "details": "desk"
        },
        {
        "name" : "subProduct2",
        "mrp" : 89,
        "details": "chair"
        }
    ]
}]

应根据给定的权限数组过滤结果对象

var permissionArray = ['logistics','paymentStatus','details.street','subOrders.name','subOrders.details'];

过滤后的结果应该是这样的

{
   result = [{
        "logistics" : 0,
        "paymentStatus" : "Paid",
        "details" : {
             "street": "S.S road",
        },
        "subOrders" : [
            {
               "name" : "sub Product1",
               "details" : "desk"
            },
            {
               "name" : "sub Product1",
               "details" : "chair"
             }
         ]
}]
}

【问题讨论】:

标签: javascript arrays node.js json lodash


【解决方案1】:

以@radulle 的回答为基础,允许使用所需的“details.name”点表示法:

(也更新为现在允许一个字段的多个子字段 - 例如“subOrders.name”和“subOrders.details”)

var arr = 
[{
    "createdBy":"Tom",
    "logistics" : 0,
    "paymentStatus" : "Paid",
    "orderAmount" : 5393.75,
    "details" : {
         "street": "S.S road",
         "postCode": "440111"
    },
    "subOrders" : [
         {
        "name" : "sub Product1",
        "mrp" : 12,
        "details": "desk"
        },
        {
        "name" : "subProduct2",
        "mrp" : 89,
        "details": "chair"
        }
    ]
}]


var permissionArray = ['logistics','paymentStatus','details.street', 'details.postCode','subOrders.name', 'subOrders.details'];

var arrFiltered = arr.map(el => {
  let newEl = {}
  for (let elm of permissionArray){
    if (elm.includes('.')){
    
      //console.log(elm);
      const split_arr = elm.split('.')
      //console.log(el[split_arr[0]]);
      
      if (el[split_arr[0]] instanceof Array){
      
        if (!newEl.hasOwnProperty([split_arr[0]]) ){
          newEl[split_arr[0]] = el[split_arr[0]].map((child,index) => ({[split_arr[1]]: child[split_arr[1]]}) )
          
        } else {
          el[split_arr[0]].forEach((child,index) => {
            //console.log(child[ split_arr[1] ]);            
            newEl[split_arr[0]][index][split_arr[1]] = child[split_arr[1]];
          })
        }
        
      } else{

        if (!newEl.hasOwnProperty([split_arr[0]]) ){
          newEl[split_arr[0]] = {[split_arr[1]]: el[split_arr[0]][split_arr[1]]}
          
        } else {
          newEl[split_arr[0]][split_arr[1]] = el[split_arr[0]][split_arr[1]];
          
        }      
      }
                
    } else {
      newEl[elm] = el[elm];
    }    
  }
  return newEl;
  }
)

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

注意:你需要先检查一个点是否包含在elm.includes('.')中,然后你必须用elm.split('.')将它分成两部分,最后你需要检查键的值是否是一个数组, 像 'subOrders' 或者它只是一个像 'details' 这样的对象,并通过直接调用子键或映射数组并调用子键来处理每种情况。

权限数组为:

var permissionArray = ['logistics','paymentStatus','details.street', 'details.postCode','subOrders.name', 'subOrders.details'];

输出是:

[
  {
    "logistics": 0,
    "paymentStatus": "Paid",
    "details": {
      "street": "S.S road",
      "postCode": "440111"
    },
    "subOrders": [
      {
        "name": "sub Product1",
        "details": "desk"
      },
      {
        "name": "subProduct2",
        "details": "chair"
      }
    ]
  }
]

注意:根据 OP 的要求,这现在还允许您拥有一个字段的两个子字段,例如 'subOrders.name', 'subOrders.details'

为此,!newEl.hasOwnProperty([split_arr[0]]) 用于检查属性/键是否不存在。如果没有,则创建它,但如果有,则修改此现有属性/键的值/项。

【讨论】:

  • 感谢您的快速回复,但稍作修改。如果我们在permissionArray 中提供多个数组参数,则该代码不会处理场景。比如在权限数组中添加 ['subOrders.name','subOrders.mrp'] 。它只会过滤 subOrders.mrp
  • 你应该在你的问题中这么说,并附上一个例子。我不知道。我可以尝试通过检查 arrFiltered 中的当前键是否已经存在来添加它。
  • 好的,现在它也可以处理这个问题了 :)
【解决方案2】:

您可以采用两步方法,将相同的键分组到一个数组中,并将嵌套属性也存储到分组的父级中。

如果您有相同的父键,这很重要,例如

['subOrders.name', 'subOrders.mrp']

例如,分组数组如下所示:

[
    [
        "logistics"
    ],
    [
        "paymentStatus"
    ],
    [
        "details",
        ["street"]
    ],
    [
        "subOrders",
        ["name", "mrp"]
    ]
]

然后根据键和嵌套键构建新对象。

function filter(keys) {

    var pathes = keys.reduce((r, path) => {
        var [key, ...rest] = path.split('.'),
            temp = r.find(([q]) => q === key);

        if (rest.length)
            if (temp) temp[1].push(rest.join('.'));
            else r.push([key, [rest.join('.')]]);
        else
            if (!temp) r.push([key]);

        return r;
    }, []);

    return function (object) {
        const keys = pathes.filter(([key]) => key in object);
        return Object.fromEntries(keys.map(([key, rest]) => [
            key,
            rest
                ? Array.isArray(object[key])
                    ? object[key].map(filter(rest))
                    : filter(rest)(object[key])
                : object[key]
        ]));
    };
}

var permissionArray = ['foo', 'logistics', 'paymentStatus', 'details.street', 'subOrders.name'],
    data = [{ createdBy: "Tom", logistics: 0, paymentStatus: "Paid", orderAmount: 5393.75, details: { street: "S.S road", postCOde: "440111", }, subOrders: [{ name: "sub Product1", mrp: 12 }, { name: "subProduct2", mrp: 89 }] }],
    result = data.map(filter(permissionArray));

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

【讨论】:

  • 工作得很好,但如果有任何字段存在于 permissionArray 而不是数据中,那么它会将该键设为空白值,这会产生问题。
  • stackoverflow.com/a/60645153/9792594@grodzi 下面的回答在我看来是最完整和最笼统的。我认为它可以处理您所有的边缘情况等。
  • @Prakashrathod,它现在也过滤未知属性。请参阅编辑。
【解决方案3】:

我们可以对原始 obj 进行操作并移除其 props,而不是构建过滤输出。

权限代表一棵树(如果节点是单词(.delimited),则表示树)

如果我们进一步假设任何指定的路径都可以在数据中访问,那么任何叶子的父级都有一个数组或对象(等等)。

然后我们构建一棵树,并为任何节点遍历数据并删除不属于我们规范的键。

const data = {"createdBy":"Tom","logistics":0,"paymentStatus":"Paid","orderAmount":5393.75,"details":{"street":"S.S road","postCOde":"440111"},"subOrders":[{"name":"sub Product1","mrp":12},{"name":"subProduct2","mrp":89}],"a":{"b":[{"c":3,"d":"a"},{"c":4,"e":"f"}]}}
const d = JSON.parse(JSON.stringify(data)) // do not mutate original
const v = ['logistics','paymentStatus','details.street','subOrders.name','subOrders.details','a.b.c']
const tree = v.map(x => x.split('.')).reduce((o, p) => {
  p.reduce((c, x) => (c[x] = c[x] || {}), o)
  return o
},{})
function rec (keep, node) {
  if (Object.keys(keep).length === 0) return
  if (Array.isArray(node)) { return node.forEach(rec.bind(0, keep)) }
  for (const k in node) {
    !(k in keep) ? delete node[k] : rec(keep[k], node[k])
  }
}
rec(tree, d)
console.log(JSON.stringify(d,null,2))

【讨论】:

  • 这太棒了,应该是公认的答案。投票赞成。
  • 无论是否被接受,感谢您的反馈@alex L!
【解决方案4】:

你可以这样做:

var arr = 
[{
    "createdBy":"Tom",
    "logistics" : 0,
    "paymentStatus" : "Paid",
    "orderAmount" : 5393.75,
    "details" : {
         "street": "S.S road",
         "postCOde": "440111"
    },
    "subOrders" : [
        {
        "name" : "sub Product1",
        "mrp" : 12
        },
        {
        "name" : "subProduct2",
        "mrp" : 89
        }
    ]
}]


var permissionArray = ['logistics','paymentStatus','details']

var arrFiltered = arr.map(el => {
  let newEl = {}
  for (let elm of permissionArray) newEl[elm] = el[elm]
  return newEl
  }
)

console.log(arrFiltered)

【讨论】:

  • 在permissionArray中可以再添加一个过滤器'subOrders.name'。其中 subOrders 是一个 json 对象数组
  • 查看我的答案@Prakashrathod,我就是这样做的 :) 基于 radulle 的出色答案
【解决方案5】:

在下面的解决方案中,我们将对象和普通数组的处理分开。

当我们沿着对象的属性递归地下降到一个对象时,我们建立了一个与权限数组中的等效字符串相对应的“限定键”。

如果我们命中一个原始值,那么我们就结束该属性的递归。

为每个递归级别创建一个新对象sanitized,以仅包含允许的属性。

const data = [{ "createdBy": "Tom", "logistics": 0, "paymentStatus": "Paid", "orderAmount": 5393.75, "details": { "street": "S.S road", "postCOde": "440111" }, "subOrders": [{ "name": "sub Product1", "mrp": 12 }, { "name": "subProduct2", "mrp": 89 }] }]

const isPrimitive = (x)=>x === null || typeof x !== "object"

function sanitize(o, permissions, prefix='') {
    if (isPrimitive(o)) return o
    const sanitized = {}
    for (const[k,v] of Object.entries(o)) {
        const qk = `${prefix}${k}`
        if (permissions.some((p)=>p.match(new RegExp(`^${qk}`)))) {
            sanitized[k] = Array.isArray(v) 
                ? sanitizeArray(v, permissions, `${qk}.`) 
                : sanitize(v, permissions, `${qk}.`)
        }
    }
    return sanitized
}

const sanitizeArray = (arr,permissions,prefix='')=>arr.map((el)=>sanitize(el, permissions, prefix))

const permissions = ['logistics', 'paymentStatus', 'details.street', 'subOrders.name']
const sanitized = sanitizeArray(data, permissions)
console.log(sanitized)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-07
    • 1970-01-01
    • 2022-01-23
    • 2022-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    相关资源
    最近更新 更多