Как установить данные в глубоко вложенный массив - PullRequest
5 голосов
/ 03 марта 2020

Я использую DevExtreme React Grid Tree. Мой первоначальный вызов заполняет только строку root, каждая дополнительная подстрока применяется по щелчку. У меня возникают проблемы при применении данных подстроки, когда есть много строк вложенной таблицы. Мне нужен эффективный способ найти правильную родительскую строку и добавить следующий вложенный массив. Вот данные таблицы с одной вложенной строкой, которую я уже добавил.

    [
  {
    "area": "Artesia",
    "list_id": 45,
    "rowId": 158324175700860960,
    "parentRowId": 0,
    "items": [
      {
        "area": "Other",
        "list_id": 15003,
        "rowId": 158324179061139520,
        "parentRowId": 158324175700860960
      }
    ]
  },
  {
    "area": "Corpus Christi",
    "list_id": 60,
    "rowId": 158324175700847800,
    "parentRowId": 0,
    "items": []
  },
  {
    "area": "Midland",
    "list_id": 10,
    "rowId": 158324175700867700,
    "parentRowId": 0,
    "items": [
      {
        "area": "Delaware Basin",
        "list_id": 11001,
        "rowId": 158324181266273440,
        "parentRowId": 158324175700867700,
        "items": []
      }
    ]
  },
  {
    "area": "San Antonio",
    "list_id": 63,
    "rowId": 158324175700814050,
    "parentRowId": 0,
    "items": []
  }
]

После нажатия на строку Midland я применил данные возврата API в качестве элемента вложенного массива.

    [
  {
    "area": "Delaware Basin",
    "list_id": 11001,
    "rowId": 158324181266273440,
    "parentRowId": 158324175700867700,
    "items": []
  }
]

Мне нужна функция, которая может l oop через данные таблицы с уровня root до неограниченного количества вложенных строк. Теперь я сопоставляю данные, используя parentId для соответствия rowId.

// root table data with one nested row applied to Midland
const tableData = [
  {
    "area": "Artesia",
    "list_id": 45,
    "rowId": 158324175700860960,
    "parentRowId": 0,
    "items": [
      {
        "area": "Other",
        "list_id": 15003,
        "rowId": 158324179061139520,
        "parentRowId": 158324175700860960
      }
    ]
  },
  {
    "area": "Corpus Christi",
    "list_id": 60,
    "rowId": 158324175700847800,
    "parentRowId": 0,
    "items": []
  },
  {
    "area": "Midland",
    "list_id": 10,
    "rowId": 158324175700867700,
    "parentRowId": 0,
    "items": [
      {
        "area": "Delaware Basin",
        "list_id": 11001,
        "rowId": 158324181266273440,
        "parentRowId": 158324175700867700,
        "items": []
      }
    ]
  },
  {
    "area": "San Antonio",
    "list_id": 63,
    "rowId": 158324175700814050,
    "parentRowId": 0,
    "items": []
  }
]

// return data from API after clicking on Delaware Basin
const returnData = [
  {
    "area": "Delaware Basin Nm",
    "list_id": 11007,
    "rowId": 158324182577224580,
    "parentRowId": 158324181266273440
  },
  {
    "area": "Delaware Basin Tx",
    "list_id": 11002,
    "rowId": 158324182577248960,
    "parentRowId": 158324181266273440
  }
]

function applyNestedData (tableData, returnData) {

}

applyNestedData(tableData, returnData)

Ответы [ 5 ]

5 голосов
/ 07 марта 2020

Вы можете просто пройти по дереву, используя некоторые dfs al go

const tableData = [{ area: 'Artesia ', list_id: 45, rowId: 15836049402342958, parentRowId: 0, Premium: '785', 'Non Premium': '152', Total: '937', items: [] }, { area: 'Corpus Christi ', list_id: 60, rowId: 158360494023429300, parentRowId: 0, Total: '3,098', items: [] }, { area: 'Denver ', list_id: 30, rowId: 158360494023563870, parentRowId: 0, Total: '7,938', items: [] }, { area: 'Fort Worth ', list_id: 14, rowId: 158360494023592130, parentRowId: 0, Total: '14', items: [{ area: 'Southlake', list_id: 1256788, rowId: 12345, parentRowId: 158360494023592130, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [] }] }, { area: 'Midland ', list_id: 10, rowId: 158360494023510200, parentRowId: 0, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [{ area: 'Delaware Basin', list_id: 11001, rowId: 158324181266273440, parentRowId: 158360494023510200, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [] }] }, { area: 'Okc ', list_id: 50, rowId: 158360494023542430, parentRowId: 0, Total: '245', items: [] }, { area: 'San Antonio ', list_id: 63, rowId: 158360494023591680, parentRowId: 0, Total: '4,162', items: [] }]
const returnData = [{ area: 'Delaware Basin Nm', list_id: 11007, rowId: 158324182577224580, parentRowId: 158324181266273440 }, { area: 'Delaware Basin Tx', list_id: 11002, rowId: 158324182577248960, parentRowId: 158324181266273440 }, { area: 'Sub Southlake', list_id: 2345, rowId: 550987654, parentRowId: 12345 }]

const byParentRowId = returnData.reduce((m, it) => {
  const v = m.get(it.parentRowId) || []
  v.push(it)
  m.set(it.parentRowId, v)
  return m
}, new Map())


const findRow = (tableData => {
  function dfs (data, stopId) {
    if (data.rowId === stopId) return data
    if (!Array.isArray(data.items)) return []
    return data.items.flatMap(x => dfs(x, stopId))
  }
  return rowId => dfs({ items: tableData }, rowId)[0]
})(tableData)

console.time('setTree1')
;[...byParentRowId.entries()].forEach(([rowId, v]) => (findRow(rowId).items = v))
console.timeEnd('setTree1')
console.log(JSON.stringify({ items: tableData }, null, 2))

Обратите внимание, что для каждого отдельного parentRowId вы пересекаете дерево. Если вы хотите немного разбираться в этом, что достигается ценой большего количества кода, вы можете просто предварительно создать узел отображения rowId => и поддерживать его при заполнении ваших вложенных строк. Я бы посоветовал не делать этого, кроме случаев, когда вы наблюдаете заметную (и значимую) выгоду. Здесь, это 1 мс, так бесполезно.

const tableData = [{ area: 'Artesia ', list_id: 45, rowId: 15836049402342958, parentRowId: 0, Premium: '785', 'Non Premium': '152', Total: '937', items: [] }, { area: 'Corpus Christi ', list_id: 60, rowId: 158360494023429300, parentRowId: 0, Total: '3,098', items: [] }, { area: 'Denver ', list_id: 30, rowId: 158360494023563870, parentRowId: 0, Total: '7,938', items: [] }, { area: 'Fort Worth ', list_id: 14, rowId: 158360494023592130, parentRowId: 0, Total: '14', items: [{ area: 'Southlake', list_id: 1256788, rowId: 12345, parentRowId: 158360494023592130, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [] }] }, { area: 'Midland ', list_id: 10, rowId: 158360494023510200, parentRowId: 0, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [{ area: 'Delaware Basin', list_id: 11001, rowId: 158324181266273440, parentRowId: 158360494023510200, Premium: '7,743', 'Non Premium': '2,584', Total: '10,327', items: [] }] }, { area: 'Okc ', list_id: 50, rowId: 158360494023542430, parentRowId: 0, Total: '245', items: [] }, { area: 'San Antonio ', list_id: 63, rowId: 158360494023591680, parentRowId: 0, Total: '4,162', items: [] }]
const returnData = [{ area: 'Delaware Basin Nm', list_id: 11007, rowId: 158324182577224580, parentRowId: 158324181266273440 }, { area: 'Delaware Basin Tx', list_id: 11002, rowId: 158324182577248960, parentRowId: 158324181266273440 }, { area: 'Sub Southlake', list_id: 2345, rowId: 550987654, parentRowId: 12345 }]

const byParentRowId = returnData.reduce((m, it) => {
  const v = m.get(it.parentRowId) || []
  v.push(it)
  m.set(it.parentRowId, v)
  return m
}, new Map())

const table = (tableData => {
  const rows = new Map()
  function dfs (data) {
    if (data.rowId) {
      rows.set(data.rowId, data)
    }
    if (!Array.isArray(data.items)) { return }
    return data.items.forEach(dfs)
  }
  dfs({ items: tableData })
  return {
    setRow: (rowId, items) => {
      items.forEach(it => rows.set(it.rowId, it))
      const row = rows.get(rowId)
      row.items = items
    },
    getRow: rowId => rows.get(rowId)
  }
})(tableData)

console.time('setTree2')
;[...byParentRowId.entries()].forEach(([rowId, v]) => table.setRow(rowId, v))
console.timeEnd('setTree2')
console.log(JSON.stringify({items: tableData},null,2))
3 голосов
/ 06 марта 2020

Я написал решение этой проблемы. Надеюсь, вам понравится.

Пожалуйста, проверьте, что я создал в стеке: https://stackblitz.com/edit/js-wurii6

const tableData = [
  {
    "area": "Artesia",
    "list_id": 45,
    "rowId": 158324175700860960,
    "parentRowId": 0,
    "items": [
      {
        "area": "Other",
        "list_id": 15003,
        "rowId": 158324179061139520,
        "parentRowId": 158324175700860960
      }
    ]
  },
  {
    "area": "Corpus Christi",
    "list_id": 60,
    "rowId": 158324175700847800,
    "parentRowId": 0,
    "items": []
  },
  {
    "area": "Midland",
    "list_id": 10,
    "rowId": 158324175700867700,
    "parentRowId": 0,
    "items": [
      {
        "area": "Delaware Basin",
        "list_id": 11001,
        "rowId": 158324181266273440,
        "parentRowId": 158324175700867700,
        "items": []
      }
    ]
  },
  {
    "area": "San Antonio",
    "list_id": 63,
    "rowId": 158324175700814050,
    "parentRowId": 0,
    "items": []
  }
];
const returnData = [
    {
      "area": "Delaware Basin Nm",
      "list_id": 11007,
      "rowId": 158324182577224580,
      "parentRowId": 158324181266273440
    },
    {
      "area": "Delaware Basin Tx",
      "list_id": 11002,
      "rowId": 158324182577248960,
      "parentRowId": 158324181266273440
    }
  ];
  

 const appResult = document.getElementById('result');

const inputWrapper = {items: tableData};
applyNestedData(inputWrapper, returnData);


function applyNestedData(tableData, returnData){
  const { parentRowId } = returnData[0];
  const datareturned = findAndUpdate(tableData, returnData, parentRowId);
  appResult.innerHTML = JSON.stringify(tableData, undefined, 4)
}

function findAndUpdate(row, returnData, parentRowId){
  if(row.rowId && row.rowId == parentRowId){
      return true;
  } else if (row.items){
      let isParent = false;
      for(let i=0; row.items && i < row.items.length; i++){
        isParent = findAndUpdate(row.items[i], returnData, parentRowId);
        if (isParent === true) {
          row.items[i].items = returnData;
          console.info("found")
          break;
        }
      }
  }
  return row;
}
Result
 
2 голосов
/ 06 марта 2020
function applyNestedData (tableData, returnData) {
  const map = new Map();
  returnData.map(data=> {
    map.has(data.parentRowId)?
      map.get(data.parentRowId).push(data):map.set(data.parentRowId, [data])
  });
  const queue = [];
  tableData.map(data=>{queue.push(data)});

  let data;
  while (queue.length > 0) {
    data = queue.shift();
    map.has(data.rowId) && (data.items = data.items.concat(map.get(data.rowId)));
    (data.items && data.items.length > 1) && data.items.map(item=> {queue.push(item)})
  }
}
2 голосов
/ 06 марта 2020
function applyNestedData (tableData, returnData) {
  // use a map to group data by its parentRowId
  // in order to insert them together when find matched rowId
  const map = new Map()
  for (const data of returnData) {
    if (map.has(data.parentRowId)) {
      map.get(data.parentRowId).push(data)
    } else {
      map.set(data.parentRowId, [data])
    }
  }

  // Loop tree-structure data by queue
  const queue = []
  for (const data of tableData) { queue.push(data) }

  let data
  while (queue.length > 0) {
    data = queue.shift()
    if (map.has(data.rowId)) {
      data.items = data.items.concat(map.get(data.rowId))
    }
    if (data.items && data.items.length > 1) {
      for (const item of data.items) {
        queue.push(item)
      }
    }
  }
}
0 голосов
/ 12 марта 2020

Вы можете создать рекурсивную функцию, подобную этой.

let iterate = (inputdata, item) => {
  let id = inputdata.parentRowId;
  if (item.rowId == id) {
     item.items.push(inputdata);
  }
  else if (item.items && item.items.length) {
    item.items.map(obj => {
      iterate(inputdata, obj);
    });
  }
}

returnData.forEach(inputdata => {
  tableData.forEach(item => {
    iterate(inputdata, item);
  });
});
...