【问题标题】:Massage JSON response to fit into treeData structure for react-simple-tree-menu按摩 JSON 响应以适应 react-simple-tree-menu 的 treeData 结构
【发布时间】:2020-08-06 17:03:56
【问题描述】:

我有一个 React 组件,它通过 HTML 端点从 REST API 检索对象数组(键值对):

[
  { 
    "id": 1,
    "grouping1": "first-level-node-1",
    "grouping2": "second-level-node-1",
    "theThing": "third-level-node-1",
    "someData": "data here that is associated with theThing",
    "someUrl": "http://someurl.com"
  },
  {
    "id": 2,
    "grouping1": "first-level-node-2",
    "grouping2": "second-level-node-1",
    "theThing":  "third-level-node-1",
    .
    .
    .
  }
]

我正在尝试操纵 JSON 响应,以便使用 react-simple-tree-menu 显示它。要生成TreeMenu,需要以数组的形式提供数据:

// as an array
const treeData = [
  {
    key: 'first-level-node-1',
    label: 'Node 1 at the first level',
    ..., // any other props you need, e.g. url
    nodes: [
      {
        key: 'second-level-node-1',
        label: 'Node 1 at the second level',
        nodes: [
          {
            key: 'third-level-node-1',
            label: 'Last node of the branch',
            nodes: [] // you can remove the nodes property or leave it as an empty array
          },
        ],
      },
    ],
  },
  {
    key: 'first-level-node-2',
    label: 'Node 2 at the first level',
  },
];

或作为一个对象:

// or as an object
const treeData = {
  'first-level-node-1': {               // key
    label: 'Node 1 at the first level',
    index: 0, // decide the rendering order on the same level
    ...,      // any other props you need, e.g. url
    nodes: {
      'second-level-node-1': {
        label: 'Node 1 at the second level',
        index: 0,
        nodes: {
          'third-level-node-1': {
            label: 'Node 1 at the third level',
            index: 0,
            nodes: {} // you can remove the nodes property or leave it as an empty array
          },
        },
      },
    },
  },
  'first-level-node-2': {
    label: 'Node 2 at the first level',
    index: 1,
  },
};

我尝试根据 grouping1(第一级节点)和 grouping2(第二级节点)对 JSON 响应进行分类,使其“适合”到 treeData:

  const fetchItems = async () => {
    const data = await fetch('http://localhost:3001/stuff');
    const input = await data.json();
    const output = input.reduce((acc, item) => ({
      ...acc,
      [item.grouping1]: {
        ...acc[item.grouping1],
        [item.grouping2]: [
          ...(acc[item.gropuing1] && acc[item.grouping1][item.grouping2] || []),
          item,
        ]
      }
    }), {})

现在我的对象 (grouping1) 包含包含键值对数组的对象 (grouping2)。

  first-level-node-1:
    second-level-node-1: Array(4)
      0: {id: 1, grouping1: "first-level-node-1", grouping2: "second-level-node-1", theThing: "third-level-node-1"}
      .
      .
      .
  first-level-node-2:
    second-level-node-1: Array(16)
      0: {id: 2, grouping1: "first-level-node-2", grouping2: "second-level-node-1", theThing: "third-level-node-1"}
      .
      .
      .

但这不是 react-simple-tree-menu 想要的 treeData 结构。如何按摩 JSON 响应以适应 treeData 结构?

这里有一个很好的write-up,介绍了如何在 React 中启动和运行侧边栏菜单,但没有任何内容说明如何获得典型的 JSON 响应以适应所需的结构。

更新:

以下是TreeMenu数据that controls this react-simple-tree-menu component

<TreeMenu
  data={[
    {
      key: 'mammal',
      label: 'Mammal',
      nodes: [
        {
          key: 'canidae',
          label: 'Canidae',
          nodes: [
            {
              key: 'dog',
              label: 'Dog',
              nodes: [],
              url: 'https://www.google.com/search?q=dog'
            },
            {
              key: 'fox',
              label: 'Fox',
              nodes: [],
              url: 'https://www.google.com/search?q=fox'
            },
            {
              key: 'wolf',
              label: 'Wolf',
              nodes: [],
              url: 'https://www.google.com/search?q=wolf'
            }
          ],
          url: 'https://www.google.com/search?q=canidae'
        }
      ],
      url: 'https://www.google.com/search?q=mammal'
    },
    {
      key: 'reptile',
      label: 'Reptile',
      nodes: [
        {
          key: 'squamata',
          label: 'Squamata',
          nodes: [
            {
              key: 'lizard',
              label: 'Lizard',
              url: 'https://www.google.com/search?q=lizard'
            },
            {
              key: 'snake',
              label: 'Snake',
              url: 'https://www.google.com/search?q=snake'
            },
            {
              key: 'gekko',
              label: 'Gekko',
              url: 'https://www.google.com/search?q=gekko'
            }
          ],
          url: 'https://www.google.com/search?q=squamata'
        }
      ],
      url: 'https://www.google.com/search?q=reptile'
    }
  ]}
  debounceTime={125}
  disableKeyboard={false}
  hasSearch
  onClickItem={function noRefCheck(){}}
  resetOpenNodesOnDataUpdate={false}
/>

如果我理解正确的话,Mammal 是 first-level-node-1,而 Reptile 是 first-level-node-2。 Canidae 和 Squamata 在各自的一级节点下都是二级节点 1。 Dog、Fox 和 Wolf 分别是第三级节点 1、节点 2 和节点 3。 Lizard、Snake 和 Gekko 也是第三级 node-1、node-2 和 node-3。我在这篇文章顶部使用的示例可能会令人困惑。如果是这样,我很抱歉。

这是与我正在使用的数据更相似的 JSON 数据:

[
  {
    "id": 2,
    "grouping1": "I124",
    "grouping2": "Cross_Streets",
    "theThing": "12th",
    "url": "http://url2.com"
  },
  {
    "id": 3,
    "grouping1": "I124",
    "grouping2": "Cross_Streets",
    "theThing": "13th",
    "url": "http://url3.com"
  },
  {
    "id": 4,
    "grouping1": "I124",
    "grouping2": "Cross_Streets",
    "theThing": "4th",
    "url": "http://url4.com"
  },
  {
    "id": 14,
    "grouping1": "I124",
    "grouping2": "Ramps",
    "theThing": "Ramp_A",
    "url": "http://url14.com"
  },
  {
    "id": 15,
    "grouping1": "I124",
    "grouping2": "Ramps",
    "theThing": "Ramp_B",
    "url": "http://url15.com"
  },
  {
    "id": 41,
    "grouping1": "I75",
    "grouping2": "Cross_Streets",
    "theThing": "100th",
    "url": "http://url41.com"
  }
]

目标是让上面的 JSON 在 react-simple-tree-menu 中看起来像这样:

+ I124
    + Cross_Streets
        12th
        13th
        4th
    + Ramps
        Ramp_A
        Ramp_B
+ I75
    + Cross_Streets
        4th

【问题讨论】:

  • 是不是应该只有三层以上?还有,应该算是一个等级吗?我看到grouping1grouping2theThingsomeDatasomeUrl 不是?这些是应该被视为级别的键的确切名称吗?
  • someData 和 someUrl 与分组没有任何关系。只是 grouping1、grouping2 和 theThing。抱歉,如果我没有说清楚。
  • @goto1 grouping1 是最高级别(树中最左侧),一旦您展开它,它将显示其下的所有grouping2,然后theThing 将显示在下面grouping2 展开时。每个theThing 都是独一无二的。 someDatasomeUrl 只是 JSON 响应中的附加值,当用户单击 theThing 时,我将对其进行处理。很抱歉造成混乱。
  • 下面的解决方案能满足你的需要吗?
  • 是的,我删除了它,因为我发现它有误,但后来修复了它,现在它又回来了,有两个结果,一个是 array,一个是 object - 现在看看。跨度>

标签: arrays json reactjs javascript-objects


【解决方案1】:

这是一个您可以应用的可能解决方案,但是,我不确定您从哪里获得 labels,但我会留给您。

这是一个以object 为结果的示例:

const DATA = [
  {
    id: 2,
    grouping1: "I124",
    grouping2: "Cross_Streets",
    theThing: "12th",
    url: "http://url2.com"
  },
  {
    id: 3,
    grouping1: "I124",
    grouping2: "Cross_Streets",
    theThing: "13th",
    url: "http://url3.com"
  },
  {
    id: 4,
    grouping1: "I124",
    grouping2: "Cross_Streets",
    theThing: "4th",
    url: "http://url4.com"
  },
  {
    id: 14,
    grouping1: "I124",
    grouping2: "Ramps",
    theThing: "Ramp_A",
    url: "http://url14.com"
  },
  {
    id: 15,
    grouping1: "I124",
    grouping2: "Ramps",
    theThing: "Ramp_B",
    url: "http://url15.com"
  },
  {
    id: 41,
    grouping1: "I75",
    grouping2: "Cross_Streets",
    theThing: "100th",
    url: "http://url41.com"
  }
];

const resultAsObject = DATA.reduce((accumulator, item) => {
  const groupId = item["grouping1"];
  const subGroupId = item["grouping2"];
  const subGroupItemId = item["theThing"];
  const url = item["url"];

  const group = accumulator[groupId] || {
    key: groupId.toLowerCase(),
    label: groupId,
    nodes: []
  };
  const subGroup = group.nodes.find(
    item => item.key === subGroupId.toLowerCase()
  ) || {
    key: subGroupId.toLowerCase(),
    label: subGroupId,
    nodes: []
  };
  const updatedSubGroupNodes = [
    {
      key: subGroupItemId.toLowerCase(),
      label: subGroupItemId,
      url: url,
      nodes: []
    }
  ];
  const updatedSubGroup = {
    ...subGroup,
    nodes: updatedSubGroupNodes
  };
  const t1 = [...group.nodes, updatedSubGroup].reduce((acc, i) => {
    const category = acc.find(t => t.key === i.key) || {
      key: i.key,
      label: i.label,
      nodes: []
    };
    const updatedNodes = [...category.nodes, ...i.nodes];
    const updatedCategory = { ...category, nodes: updatedNodes };

    // replace the existing category object and append
    // the updated object with populated `nodes` property
    return [...acc.filter(t => t.key !== category.key), updatedCategory];
  }, []);

  const updatedGroup = {
    ...group,
    nodes: t1
  };
  return {
    ...accumulator,
    [groupId]: updatedGroup
  };
}, {});

console.log(resultAsObject);

【讨论】:

  • 请查看我原帖的更新部分。这是我使用 resultAsArray 得到的结果:postimg.cc/z3j0D1ys 我的理解是 react-simple-tree-menu 需要与唯一 grouping1 值一样多的结果(在我更新的示例中,I124 应该有一个,I75 应该有一个)。然后所有的 grouping2s 和 theThings 都会在它之下。这是我使用 resultAsObject 得到的结果:postimg.cc/bd8mjvw0 grouping1s 看起来不错,但并不是所有的 grouping2s 和 theThings 都出现了。希望这是有道理的。感谢您的宝贵时间。
  • @Rayner 知道了,我会看一下并更新答案。
  • @Rayner 看看更新后的问题,如果是这样,请告诉我。
猜你喜欢
  • 1970-01-01
  • 2019-12-18
  • 2019-11-17
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2018-05-23
  • 2022-01-28
  • 1970-01-01
相关资源
最近更新 更多