为了账号安全,请及时绑定邮箱和手机立即绑定

通过给定索引解析具有嵌套对象的数组,获取其名称

通过给定索引解析具有嵌套对象的数组,获取其名称

繁星点点滴滴 2022-09-23 16:30:20

链接到沙盒:https://codesandbox.io/s/cool-northcutt-7c9sr


我有一个带家具的目录,我需要制作动态面包屑。有一个数组,其中包含 5 层深的嵌套对象。当我渲染家具列表时,我会保存此列表数组中的所有索引。


预期输出:使用我的索引,我需要解析具有嵌套数组的对象,获取属于该索引的每个对象的名称并将其保存在数组中


用户单击清单时保存的索引。键是对象名称,属性是实际索引。


menuIndexes : {

  buildings: 0,

  building_styles: 3,

  rooms: 2,

  room_fillings: 0,

  filling_types: 1,

}

那段数据,我从家具列表渲染。名称属性是菜单中链接的名称


{

  buildings: [

    {

      name: 'office',

      building_styles: [

        {

          name: 'interior',

          rooms: [

            {

              name: 'open space',

              filling_types: [

                {

                  name: 'furniture',

                  room_fillings: [

                    {

                      name: 'items',

                       // rendering inventories

                      inventories: [


                        {

                          id: 1,

                          name: 'tv'

                        },

                        {

                          id: 2,

                          name: 'chair'

                        },

                        {

                          id: 3,

                          name: 'table'

                        },

                      ]

                    }

                  ]


                }

              ]

            }

          ]

        },

      ]

    },

  ]

}

此图像用于了解我在哪里获得此保存的索引

http://img3.sycdn.imooc.com/632d6eb90001588411050893.jpg

我试图制作递归函数,但它只得到第一个数组,不会进一步进入嵌套数组


  const displayBreadCrumbs = () => {

    let menuKeys = Object.keys(menuIndexes)

    console.log({ menuIndexes });

    console.log(file);

    let arr = []

    let i = 0

    let pathToParse = file



    if (i < menuKeys.length) {

      if (menuKeys[i] in pathToParse) {

        pathToParse = pathToParse[menuKeys[i]][menuIndexes[menuKeys[i]]]

        console.log('pathToParse', pathToParse);

        i = i + 1

        // displayBreadCrumbs()

      }

    }

  }


查看完整描述

3 回答

?
喵喵时光机

TA贡献1495条经验 获得超7个赞

您可以遍历 中的每个键值对对象,以查看哪个键属于当前对象。获得适用于当前对象的键后,您可以访问其关联的数组以及需要查看的索引。找到键值对后,可以从条目中删除键值对,然后再次以递归方式查找新对象中的下一个键。您可以继续此操作,直到找不到属于当前对象的键(即:findIndex返回-1);menuIndexespathToParsemenuIndexesdata


const pathToParse = { buildings: [ { name: 'office', building_styles: [ { name: 'interior', rooms: [ { name: 'open space', filling_types: [ { name: 'furniture', inventories: [{ id: 1, name: 'tv' }, { id: 2, name: 'chair' }, { id: 3, name: 'table' }, ] } ] } ] }, ] }, ] }


const menuIndexes = {

  buildings: 0,

  building_styles: 0,

  rooms: 0,

  room_fillings: 0,

  filling_types: 0,

}


function getPath(data, entries) {

  const keyIdx = entries.findIndex(([key]) => key in data);

  if(keyIdx <= -1)

    return [];

    

  const [objKey, arrIdx] = entries[keyIdx];

  const obj = data[objKey][arrIdx];

  entries.splice(keyIdx, 1);

  return [obj.name].concat(getPath(obj, entries)); 

}


console.log(getPath(pathToParse, Object.entries(menuIndexes)));


的用途是搜索对象以查找要查看的键(因为我们不想依赖于对象的键排序)。如果您对 有更多的控制权,则可以将其存储在一个数组中,您可以在其中安全地依赖元素的顺序,从而依赖键:Object.entries()datamenuIndexesmenuIndexes


const pathToParse = { buildings: [ { name: 'office', building_styles: [ { name: 'interior', rooms: [ { name: 'open space', filling_types: [ { name: 'furniture', inventories: [{ id: 1, name: 'tv' }, { id: 2, name: 'chair' }, { id: 3, name: 'table' }, ] } ] } ] }, ] }, ] };


const menuIndexes = [{key: 'buildings', index: 0}, {key: 'building_styles', index: 0}, {key: 'rooms', index: 0}, {key: 'filling_types', index: 0}, {key: 'inventories', index: 0}, ];


function getPath(data, [{key, index}={}, ...rest]) {

  if(!key)

    return [];

  const obj = data[key][index];

  return [obj.name, ...getPath(obj, rest)]; 

}


console.log(getPath(pathToParse, menuIndexes));


查看完整回答
反对 回复 2022-09-23
?
慕神8447489

TA贡献1458条经验 获得超1个赞

像往常一样,我会在一些可重用的部件上构建这样的函数。这是我的方法:


// Utility functions

const path = (obj, names) =>

  names .reduce ((o, n) => (o || {}) [n], obj)


const scan = (fn, init, xs) => 

  xs .reduce (

    (a, x, _, __, n = fn (a .slice (-1) [0], x)) => [...a, n], 

    [init]

  ) .slice (1)

  

const pluck = (name) => (xs) =>

  xs .map (x => x [name])



// Main function

const getBreadCrumbs = (data, indices) => 

  pluck ('name') (scan (path, data, Object .entries (indices)))



// Sample data

const data = {buildings: [{name: "office", building_styles: [{building_style: 0}, {building_style: 1}, {building_style: 2}, {name: "interior", rooms: [{room: 0}, {room: 1}, {name: "open space", filling_types: [{filling_type: 0}, {name: "furniture", room_fillings: [{name: "items", inventories: [{id: 1, name: "tv"}, {id: 2, name: "chair"}, {id: 3, name: "table"}]}, {room_filling: 1}]}, {filling_type: 2}]}, {room: 3}]}, {building_style: 4}]}]}

const menuIndexes = {buildings: 0, building_styles: 3, rooms: 2, filling_types: 1, room_fillings: 0}



// Demo

console .log (getBreadCrumbs (data, menuIndexes))

我们这里有三个可重用的函数:

  • path 采用一个对象和节点名称(字符串或整数)列表,并在给定路径处返回值,或者如果缺少任何节点,则返回值。1 例如:undefined

    path ({a: {b: [{c: 10}, {c: 20}, {c: 30}, {c: 40}]}}, ['a', 'b', 2, 'c']) //=> 30.
  • scan 与 非常相似,不同之处在于它不返回最终值,而是返回在每个步骤中计算的值的列表。例如,如果 是 ,则:Array.prototype.reduceadd(x, y) => x + y

    scan (add, 0, [1, 2, 3, 4, 5]) //=> [1, 3, 6, 10, 15]
  • 拔出将命名属性从每个对象列表中拉出:

    pluck ('b') ([{a: 1, b: 2, c: 3}, {a: 10, b: 20, c: 30}, {a: 100, b: 200, c: 300}]) 
    //=> [2, 20, 200]

在实践中,我实际上会进一步考虑这些帮助器,根据 定义来定义,并在定义中使用和。但这对这个问题并不重要。pathconst prop = (obj, name) => (obj || {}) [name]const last = xs => xs.slice (-1) [0]const tail = (xs) => xs .slice (-1)scan

然后,我们的 main 函数可以简单地使用这些条目以及 2,首先从索引中获取条目,将该条目、我们的函数和数据传递给相关对象节点的列表,然后将结果与我们要提取的字符串一起传递给。Object.entriespathscanpluck'name'

我几乎每天都使用。 不太常见,但它足够重要,它被包含在我通常的实用程序库中。有了这样的功能,编写类似 的东西就非常简单了。pathpluckscangetBreadCrumbs


1 旁注,我通常将其定义为,我发现这是最常用的。此表单恰好更适合所使用的代码,但是将代码调整为我的首选形式很容易:而不是 ,我们可以只编写(names) => (obj) => ...scan (path, data, ...)scan ((a, ns) => path (ns) (a), data, ...)

2 正如尼克·帕森斯(Nick Parsons)的回答和感谢的评论所指出的那样,将此信息存储在一个显式排序的数组中是有一个很好的论据,而不是依赖于一般对象获得的奇怪和任意的顺序。如果这样做,则此代码只能通过删除 main 函数中的调用来更改。Object .entries


查看完整回答
反对 回复 2022-09-23
?
慕斯709654

TA贡献1521条经验 获得超5个赞

您可以像这样定义一个递归函数,它循环访问键并在数据中找到相应的对象。找到数据后,它会将名称推送到输出数组中,并使用此对象再次调用该函数,并且menuIndexesmenuIndexes


const displayBreadCrumbs = (data, menuIndexes) => {

    const output = [];

    Object.keys(menuIndexes).forEach(key => {

        if (data[key] && Array.isArray(data[key]) && data[key][menuIndexes[key]]) {

            output.push(data[key][menuIndexes[key]].name);

            output.push(...displayBreadCrumbs(data[key][menuIndexes[key]], menuIndexes));

        }

    });

    return output;

};

const data = { buildings: [ { name: 'office', building_styles: [ { name: 'interior', rooms: [ { name: 'open space', filling_types: [ { name: 'furniture', inventories: [{ id: 1, name: 'tv' }, { id: 2, name: 'chair' }, { id: 3, name: 'table' }, ] } ] } ] }, ] }, ] };


const menuIndexes = {

  buildings: 0,

  building_styles: 0,

  rooms: 0,

  room_fillings: 0,

  filling_types: 0,

};


displayBreadCrumbs(data, menuIndexes); // ["office", "interior", "open space", "furniture"]


查看完整回答
反对 回复 2022-09-23
  • 3 回答
  • 0 关注
  • 12 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信