Как обновить свойство в глубоко вложенном массиве объектов неизвестного размера, используя Javascript? - PullRequest
1 голос
/ 29 марта 2020

У меня есть большой массив объектов, которые мне нужно рекурсивно пройти через oop и найти объект, который мне нужно обновить, по его uid и обновить свойство data внутри этого объекта. Этот массив имеет неизвестный размер и форму. Следующий метод работает, но мне было интересно, есть ли «более чистый» способ сделать этот процесс.

Следующий код перебирает каждый объект / массив, пока не найдет объект со свойством uid, если это uid соответствует блоку, который мне нужно обновить, я установил свойство data этого объекта. Есть ли лучший способ сделать это?

Метод обхода

      function traverse(x) {
        if (isArray(x)) {
          traverseArray(x)
        } else if ((typeof x === 'object') && (x !== null)) {
          traverseObject(x)
        } else {

        }
      }

      function traverseArray(arr) {
        arr.forEach(function (x) {
          traverse(x)
        })
      }

      function traverseObject(obj) {
        for (var key in obj) {

          // check if property is UID

          if (key == "uid") {

            // check if this uid is equal to what we are looking for

            if (obj[key] == "bcd") {

              // confirm it has a data property

              if (obj.hasOwnProperty('data')) {

                // assign new data to the data property

                obj['data'] = newData;
                return;
              }
            }
          }
          if (obj.hasOwnProperty(key)) {
            traverse(obj[key])
          }
        }
      }

      function isArray(o) {
        return Object.prototype.toString.call(o) === '[object Array]'
      }

      // usage:
      traverse(this.sections)

Пример данных

this.sections = [
  {
    uid: "abc",
    layout: {
        rows: [
            {
                columns: [
                    {
                        blocks: [
                            {
                                uid: "bcd",
                                data: {
                                    content: "how are you?"
                                }
                            }
                        ]
                    },
                    {
                        blocks: [
                            {
                                uid: "cde",
                                data: {
                                    content: "how are you?"
                                }
                            }
                        ]
                    },
                ],
            },
            {
                columns: [
                    {
                        blocks: [
                            {
                                uid: "def",
                                data: {
                                    content: "how are you?"
                                }
                            }
                        ]
                    }
                ],
            }
        ]
    }
  }
]

Is Есть ли лучший способ справиться с этим? Спасибо!

Ответы [ 2 ]

1 голос
/ 29 марта 2020

Я бы порекомендовал начать с универсальной c функции отображения объектов. Для этого требуется ввод объекта o и преобразование t -

const identity = x =>
  x

const mapObject = (o = {}, t = identity) =>
  Object.fromEntries(Object.entries(o).map(([ k, v ]) => [ k, t(v) ]))

. Затем создайте свою функцию рекурсивного сопоставления объектов, используя вашу мелкую функцию. Для преобразования требуется объект o и функция преобразования t -

const recMapObject = (o = {}, t = identity) =>
  mapObject                       // <-- using mapObject
    ( o
    , node =>
        Array.isArray(node)      // <-- recur arrays
          ? node.map(x => recMapObject(t(x), t))
      : Object(node) === node    // <-- recur objects
          ? recMapObject(t(node), t)
      : t(node)
    )

Теперь вы создаете преобразование объекта, уникальное для вашей программы, с помощью нашей функции рекурсивного сопоставления объектов. Для этого требуются сложные вложенные данные graph, uid для сопоставления и преобразование для применения к сопоставленному узлу t -

const transformAtUid = (graph = {}, uid = "", t = identity) =>
  recMapObject              // <-- using recMapObject
    ( graph
    , (node = {}) =>
        node.uid === uid                    // if uid matches,
          ? { ...node, data: t(node.data) } // then transform node.data,
          : node                            // otherwise no change 
    )

Вышеуказанный шаг важен, поскольку он распутывает c logi c о node.uid и node.data от остальной части универсального c кода преобразования объекта.

Теперь мы вызываем нашу функцию на входе data, чтобы преобразовать узлы, совпадающие с node.uid равными "bcd", используя пример преобразования -

const result =
  transformAtUid
    ( data                                // <-- input
    , "bcd"                               // <-- query
    , node => ({ ...node, changed: "x" }) // <-- transformation
    )

console.log(result)

Выходные данные -

{
  "uid": "abc",
  "layout": {
    "rows": [
      {
        "columns": [
          {
            "blocks": [
              {
                "uid": "bcd",
                "data": {
                  "content": "how are you?",
                  "changed": "x"             // <-- transformed
                }
              }
            ]
          }

          // ...
}

Разверните фрагмент ниже, чтобы проверить результаты в своем собственном браузере -

const identity = x =>
  x

const mapObject = (o = {}, t = identity) =>
  Object.fromEntries(Object.entries(o).map(([ k, v ]) => [ k, t(v) ]))

const recMapObject = (o = {}, t = identity) =>
  mapObject                       // <-- using mapObject
    ( o
    , node =>
        Array.isArray(node)      // <-- recur arrays
          ? node.map(x => recMapObject(t(x), t))
      : Object(node) === node    // <-- recur objects
          ? recMapObject(t(node), t)
      : t(node)                  // <-- transform
    )

const transformAtUid = (graph = {}, uid = "", t = identity) =>
  recMapObject
    ( graph
    , (node = {}) =>
        node.uid === uid
          ? { ...node, data: t(node.data) }
          : node
    )
    
const data =
  {uid:"abc",layout:{rows:[{columns:[{blocks:[{uid:"bcd",data:{content:"how are you?"}}]},{blocks:[{uid:"cde",data:{content:"how are you?"}}]},],},{columns:[{blocks:[{uid:"def",data:{content:"how are you?"}}]}],}]}}

const result =
  transformAtUid
    ( data
    , "bcd"
    , node => ({ ...node, changed: "x" })
    )

console.log(JSON.stringify(result, null, 2))

В вашем исходном вопросе я вижу, что у вас есть массив объектов для изменения -

// your original
this.sections = [ {...}, {...}, ... ]

Для использования recMapObject и transformAtUid с массивом объектов, мы можем использовать Array.prototype.map -

this.sections = this.sections.map(o => transformAtUid(o, "bcd", ...))
0 голосов
/ 29 марта 2020

Может быть проще, если использовать очередь:

var sections = [
  {
    uid: "abc",
    layout: {
      rows: [
        {
          columns: [
            {
              blocks: [
                {
                  uid: "bcd",
                  data: {
                    content: "how are you?"
                  }
                }
              ]
            },
            {
              blocks: [
                {
                  uid: "cde",
                  data: {
                    content: "how are you?"
                  }
                }
              ]
            }
          ]
        },
        {
          columns: [
            {
              blocks: [
                {
                  uid: "def",
                  data: {
                    content: "how are you?"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  }
];

var queue = [];
function trav(ss) {
  queue = queue.concat(ss);
  while (queue.length > 0) {
    tObj(queue.pop());
  }
  
  console.log("result", ss);
}

function tObj(obj) {
  if (obj.uid === "bcd") {
    obj.data = "new data";
  } else {
    Object.keys(obj).forEach(el => {
      if (Array.isArray(obj[el])) {
        queue = queue.concat(obj[el]);
      } else if (typeof (obj[el]) === "object") {
        tObj(obj[el]);
      }
    });
  }
}



trav(sections);
...