简体   繁体   English

根据另一个对象过滤一个对象

[英]Filter an object based on another object

I'd like to transform an object based on a dot notation string in another object.我想根据另一个对象中的点符号字符串转换一个对象。 For instance:例如:

const objToTransform = {
  "a": "aValue",
  "b": {
      "c": "cValue",
      "d": "dValue"
  }
};

const filteringObj = {
  "field1": "a",
  "field2": {
    "subfield1": "b.c",
    "subfield2": "b.d"
  }
};

const filteredObj = myFunc(objToTransform, filteringObj);
// expect outputs to be: 
//  {
//   "field1": "aValue",
//   "field2": {
//      "subfield1": "cValue",
//      "subfield2": "dValue"
//    }
//  }

I've been working on this seemingly simple thing for hours and I still can't get it to work.我已经在这个看似简单的事情上工作了几个小时,但我仍然无法让它发挥作用。 I've found this topic , which shows you how to get a nested object value with a dot notation string but couldn't get any further sadly.我找到了这个主题,它向您展示了如何使用点符号字符串获取嵌套对象值,但遗憾的是无法进一步获取。

Thanks in advance for your help!在此先感谢您的帮助!

You can do that in following steps using recursion:您可以使用递归在以下步骤中执行此操作:

  • Make a copy of the filteringObj .制作filteringObj的副本。
  • Create helper function getByPath which takes objects and path and return value at that path from nested object创建辅助函数getByPath ,它从嵌套对象中获取对象和路径并在该路径处返回值
  • Create a function transform which loops over the keys of filteringObj (I named it pattern in function)创建一个函数transform ,它循环filteringObj filtersObj 的键(我在函数中将其命名为pattern
  • In side the loop check if value is an object then call the transform function recursively on that subobject在循环旁边检查值是否是一个对象,然后在该子对象上递归调用transform函数
  • If its not a object then get the value from the original object objectToTransform by using getByPath function如果它不是一个对象,然后从原始对象的值objectToTransform使用getByPath功能

 const objToTransform = { "a": "aValue", "b": { "c": "cValue", "d": "dValue" } }; const filteringObj = { "field1": "a", "field2": { "subfield1": "bc", "subfield2": "bd" } }; function getByPath(obj, path){ //console.log(path) return path.split('.').reduce((ac,a) => ac[a], obj); } function transform(obj, pattern){ for(let key in pattern){ if(typeof pattern[key] === "object"){ transform(obj, pattern[key]); } else{ pattern[key] = getByPath(obj, pattern[key]); } } } const copy = JSON.parse(JSON.stringify(filteringObj)) transform(objToTransform, copy); console.log(copy)

You could create recursive function using reduce method that will create recursive call every time when the value is of type object.您可以使用reduce方法创建递归函数,该方法将在每次值为对象类型时创建递归调用。

 const objToTransform = { "a": "aValue", "b": { "c": "cValue", "d": "dValue" } }; const filteringObj = { "field1": "a", "field2": { "subfield1": "bc", "subfield2": "bd" } }; function transform(a, b) { return Object.entries(b).reduce((r, [k, v]) => { r[k] = typeof v !== 'object' ? v.split('.').reduce((r, e) => r[e], a) : transform(a, v) return r; }, {}) } const result = transform(objToTransform, filteringObj) console.log(result)

I have done a recursive algo:我做了一个递归算法:

 const getRefVal=(ePath, eData)=>ePath.split('.').reduce((r,x)=>r[x], eData); function myFunc(oData, oModel) { let ret = {}; setKeyVal(oModel,ret); function setKeyVal(src,res) { // recursive function for (let [k,v] of Object.entries(src)) { if (typeof src[k]==='object') { res[k] = {}; setKeyVal(v,res[k]) } else { res[k] = getRefVal(v,oData) } } } return ret } const objToTransform= { a: 'aValue', b: { c: 'cValue', d: 'dValue' } } , filteringObj = { field1: 'a', field2: { subfield1: 'b.c', subfield2: 'bd' } }; // test const filteredObj = myFunc(objToTransform, filteringObj); // proof console.log( filteredObj )

Lodash allows you to use the dot notation very easily. Lodash 允许您非常轻松地使用点符号。 Here is exactly what you want (<10 lines) :这正是您想要的(<10 行):

import _ from "lodash";

const objToTransform = {
    a: "aValue",
    b: {
        c: "cValue",
        d: "dValue"
    }
};

const filteringObj = {
    field1: "a",
    field2: {
        subfield1: "b.c",
        subfield2: "b.d"
    }
};

const filter = (model, data, filtered = {}) => {
    for (const field in model) {
        filtered[field] =
            typeof model[field] === "object"
                ? filter(model[field], data, model[field])
                : _.get(objToTransform, model[field]);
    }
    return filtered;
};

console.log(filter(filteringObj, objToTransform))

If you are using (or interested in using) Ramda (disclaimer: I'm one if its primary authors), this might work for you.如果您正在使用(或有兴趣使用) Ramda (免责声明:我是其主要作者之一),这可能对您有用。 Ramda does not have a deepMap function, but it's easy to write one. deepMap没有deepMap函数,但编写一个很容易。 And using that, your transformation function is a one-liner:使用它,您的转换函数是单行的:

 const deepMap = (fn) => (obj) => is (Object, obj) ? map (deepMap (fn)) (obj) : fn (obj) const transform = compose(applySpec, deepMap (compose (path, split('.')))) const objToTransform = {a: "aValue", b: {c: "cValue", d: "dValue"}} const filteringObj = {field1: "a", field2: {subfield1: "bc", subfield2: "bd"}} console .log (transform (filteringObj) (objToTransform))
 <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script> <script> const {is, map, compose, applySpec, path, split } = R </script>

The key point here is applySpec , which you could use directly with a slightly different format of filteringObj .这里的关键点是applySpec ,您可以直接使用它与略有不同的filteringObj格式。 This would yield your desired results:这将产生您想要的结果:

applySpec ({
  field1: path (["a"]), 
  field2: {
    subfield1: path (["b", "c"]), 
    subfield2: path (["b", "d"])
  }
}) (objToTransform)

The remainder of the transform function is simply to convert the filteringObj into that form above. transform函数的其余部分只是将filteringObj转换为上述形式。

If you want to keep calling it as asked in the question, that is as transform (objToTransform, filteringObj) rather than trasform (filteringObj) (objToTransform) , it's only slightly more complex:如果您想按照问题中的要求继续调用它,即作为transform (objToTransform, filteringObj)而不是trasform (filteringObj) (objToTransform) ,它只是稍微复杂一点:

const transform = (objToTransform, filteringObj) => 
  applySpec(deepMap (compose (path, split('.'))) (filteringObj)) (objToTransform)

You can write your own versions of those Ramda functions, it's mostly easy .您可以编写自己的 Ramda 函数版本,这很简单 But a version of applySpec which works on nested properties would be a bit more difficult.但是适用于嵌套属性的applySpec版本会有点困难。

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

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