Фильтровать объект на основе другого объекта - PullRequest
0 голосов
/ 06 марта 2020

Я хотел бы преобразовать объект на основе строки точечной нотации в другом объекте. Например:

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"
//    }
//  }

Я часами работал над этой, казалось бы, простой вещью и до сих пор не могу заставить ее работать. Я нашел эту топи c, которая показывает вам, как получить значение вложенного объекта с помощью строки точечной нотации, но, к сожалению, не может быть дальше.

Заранее спасибо за ваша помощь!

Ответы [ 5 ]

0 голосов
/ 06 марта 2020

Если вы используете (или хотите использовать) Рамда (отказ от ответственности: я один из его основных авторов), это может сработать для вас. У Рамды нет функции deepMap, но ее легко написать. И используя это, ваша функция преобразования является однострочной:

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: "b.c", subfield2: "b.d"}}

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>

Ключевым моментом здесь является applySpec, который можно использовать напрямую с немного другим форматом filteringObj. Это даст желаемые результаты:

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

Остальная часть функции transform состоит в простом преобразовании filteringObj в форму выше.

Если вы хотите продолжать вызывать ее как задано в вопросе, это как transform (objToTransform, filteringObj), а не trasform (filteringObj) (objToTransform), только немного сложнее:

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

Вы можете написать свои собственные версии этих функций Ramda, это в основном легко . Но версия applySpec, которая работает с вложенными свойствами, будет немного сложнее.

0 голосов
/ 06 марта 2020

Loda sh позволяет очень легко использовать точечную запись. Вот именно то, что вы хотите (<10 строк): </p>

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))
0 голосов
/ 06 марта 2020

Вы можете создать рекурсивную функцию, используя метод reduce, который будет создавать рекурсивный вызов каждый раз, когда значение имеет тип объекта.

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

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

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)
0 голосов
/ 06 марта 2020

Я сделал рекурсивный анализ go:

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: 'b.d' } };

// test 
const filteredObj = myFunc(objToTransform, filteringObj);
// proof
console.log( filteredObj )
0 голосов
/ 06 марта 2020

Вы можете сделать это в следующих шагах, используя рекурсию:

  • Сделать копию filteringObj.
  • Создать вспомогательную функцию getByPath, которая принимает объекты и путь и возвращает значение в этом пути из вложенного объекта
  • Создайте функцию transform, которая зацикливается на клавишах filteringObj (я назвал ее pattern в функции)
  • В стороне l oop проверьте, является ли значение объектом, а затем рекурсивно вызовите функцию transform для этого подобъекта
  • Если это не объект, получите значение из исходного объекта objectToTransform, используя getByPath function

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

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

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...