V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
rv54ntjwfm3ug8
V2EX  ›  JavaScript

从多层嵌套的 object 中搜索配对的值然后进行操作的最佳实践是什么?

  •  
  •   rv54ntjwfm3ug8 · 2022-04-14 23:34:07 +08:00 · 1914 次点击
    这是一个创建于 742 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设有数据:

    let data = [
        {
            label: 'Home',
            items: [
                { label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] }
            ]
        },
        {
            label: 'UI Components',
            items: [
                { label: 'Form Layout', icon: 'pi pi-fw pi-id-card', routerLink: ['/uikit/formlayout'] },
                { label: 'Input', icon: 'pi pi-fw pi-check-square', routerLink: ['/uikit/input'] },
                { label: 'Float Label', icon: 'pi pi-fw pi-bookmark', routerLink: ['/uikit/floatlabel'] },
                { label: 'Invalid State', icon: 'pi pi-fw pi-exclamation-circle', routerLink: ['/uikit/invalidstate'] },
                { label: 'Button', icon: 'pi pi-fw pi-mobile', routerLink: ['/uikit/button'], class: 'rotated-icon' },
                { label: 'Table', icon: 'pi pi-fw pi-table', routerLink: ['/uikit/table'] },
            ]
        },
        {
            label: 'Blocks',
            items: [
                { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'] },
                { label: 'Tree', icon: 'pi pi-fw pi-share-alt', routerLink: ['/uikit/tree'] },
                { label: 'Panel', icon: 'pi pi-fw pi-tablet', routerLink: ['/uikit/panel'] },
                
                { label: 'Google', icon: 'pi pi-fw pi-globe', url: ['https://www.google.com'], target: '_blank' },
                { label: 'Bing', icon: 'pi pi-fw pi-comment', url: ['https://www.bing.com'] },
                
                {
                    label: 'More',
                    items: [
                        { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'] },
                        
                        { label: 'Google', icon: 'pi pi-fw pi-globe', url: ['https://www.google.com'], target: '_blank' },
                        { label: 'Bing', icon: 'pi pi-fw pi-comment', url: ['https://www.bing.com'] },
                    ]
                }
            ]
        }
    ];
    

    后端返回数据:

    let badges={
        "/uikit/list": "23",
        "https://www.google.com": "NEW"
    }
    

    需要将数据更新为("/uikit/list"https://www.google.com 只有可能在某个多层嵌套项的 routerLinkurl 属性中):

    let new_data = [
        {
            label: 'Home',
            items: [
                { label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] }
            ]
        },
        {
            label: 'UI Components',
            items: [
                { label: 'Form Layout', icon: 'pi pi-fw pi-id-card', routerLink: ['/uikit/formlayout'] },
                { label: 'Input', icon: 'pi pi-fw pi-check-square', routerLink: ['/uikit/input'] },
                { label: 'Float Label', icon: 'pi pi-fw pi-bookmark', routerLink: ['/uikit/floatlabel'] },
                { label: 'Invalid State', icon: 'pi pi-fw pi-exclamation-circle', routerLink: ['/uikit/invalidstate'] },
                { label: 'Button', icon: 'pi pi-fw pi-mobile', routerLink: ['/uikit/button'], class: 'rotated-icon' },
                { label: 'Table', icon: 'pi pi-fw pi-table', routerLink: ['/uikit/table'] },
            ]
        },
        {
            label: 'Blocks',
            items: [
                { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'], badge: '23' },
                { label: 'Tree', icon: 'pi pi-fw pi-share-alt', routerLink: ['/uikit/tree'] },
                { label: 'Panel', icon: 'pi pi-fw pi-tablet', routerLink: ['/uikit/panel'] },
                
                { label: 'Google', icon: 'pi pi-fw pi-globe', url: ['https://www.google.com'], target: '_blank', badge: 'NEW' },
                { label: 'Bing', icon: 'pi pi-fw pi-comment', url: ['https://www.bing.com'] },
                
                {
                    label: 'More',
                    items: [
                        { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'], badge: '23' },
                        
                        { label: 'Google', icon: 'pi pi-fw pi-globe', url: ['https://www.google.com'], target: '_blank', badge: 'NEW' },
                        { label: 'Bing', icon: 'pi pi-fw pi-comment', url: ['https://www.bing.com'] },
                    ]
                }
            ]
        }
    ];
    

    请问除了暴力的用 forEach 一项项遍历还有什么更优雅的做法么?

    后端接口无法修改。

    14 条回复    2022-04-15 14:08:55 +08:00
    Kokororin
        1
    Kokororin  
       2022-04-15 00:11:39 +08:00 via iPhone
    deepdash
    Leviathann
        2
    Leviathann  
       2022-04-15 00:18:07 +08:00
    ...那放到 immer 里直接 flatmap 出 item 再遍历改值?
    imycc
        3
    imycc  
       2022-04-15 00:19:34 +08:00
    这个是描述目录的数据的,每个元素的规则都差不多,用递归处理,好像也不是很麻烦的样子?对于每个元素,碰到有 items 的就递归,没有的就判断 url 或者 routerLink 的值是否为目标值。
    afewok
        4
    afewok  
       2022-04-15 00:55:38 +08:00
    暴力的方式,难道不是字符串分割和拼接??
    以 '/uikit/list'将这坨字符串分割成 2 个字符串,第一个串 + ], badge: '23' +第二个去掉 ], 的串,不就可以了。
    rv54ntjwfm3ug8
        5
    rv54ntjwfm3ug8  
    OP
       2022-04-15 00:58:11 +08:00
    @afewok #4 哈哈哈最早我也想到了这种方法,就是不便于维护,会被接手的骂
    mxT52CRuqR6o5
        6
    mxT52CRuqR6o5  
       2022-04-15 01:25:23 +08:00 via Android
    肯定得遍历啊,先序遍历中序遍历后序遍历
    GeruzoniAnsasu
        7
    GeruzoniAnsasu  
       2022-04-15 02:41:40 +08:00
    先建倒排索引呗还能咋的,反正只遍历一次

    反正架构方案就要求了前端必须完整存储所有 data ,那我为了加速多建几种用于索引的数据结构谁都 blame 不了吧
    mind3x
        8
    mind3x  
       2022-04-15 02:51:30 +08:00 via Android
    jsonpath
    chnwillliu
        9
    chnwillliu  
       2022-04-15 06:39:29 +08:00 via Android
    这 routerLink 数据量能有多大?上万?十万?遍历抗得住的。

    看起来你这是要渲染导航栏,要不就不追求合并 badge 数据到树结构里?渲染导航节点的时候自己拿 url 去那个 map 里查有没有 badge 呗?
    ChefIsAwesome
        10
    ChefIsAwesome  
       2022-04-15 09:06:41 +08:00
    写俩函数,一个是把这种树形对象变成 html 。这样一来,增删改查的操作,可以直接用 dom api 。

    <item label icon router>
    <item label icon router></item>
    <item label icon router></item>
    </item>

    然后再有一个函数能把这个 html 还原成 js 对象。实际也就遍历一遍,这一个是前面那个反过来而已。
    renmu123
        11
    renmu123  
       2022-04-15 09:19:48 +08:00 via Android
    遍历就完事了
    hazardous
        12
    hazardous  
       2022-04-15 10:37:47 +08:00
    @afewok 要先把对象序列化成字符串,这个恐怕比遍历慢多了。
    Rache1
        13
    Rache1  
       2022-04-15 13:39:49 +08:00   ❤️ 1
    前段时间学到的,利用 JSON.parse 的第二个参数,试了一下,效果还可以。

    ```js
    var data2 = JSON.parse(JSON.stringify(data), function (k, v) {
    // 到顶层时返回
    if (k === '') {
    return v
    }

    if (k === 'items' && Array.isArray(v)) {
    v.forEach(function (item) {
    Object.keys(badges).forEach(function (badge) {
    // 处理 routerLink
    if (Array.isArray(item.routerLink) && item.routerLink.includes(badge)) {
    Object.assign(item, { badge: badges[badge] })
    }

    // 处理 url
    if (Array.isArray(item.url) && item.url.includes(badge)) {
    Object.assign(item, { badge: badges[badge] })
    }
    })
    })
    }
    return v
    })
    ```
    jjwjiang
        14
    jjwjiang  
       2022-04-15 14:08:55 +08:00
    遍历有啥不优雅的……直接用 array 的 api 呗

    说实话我不是很明白一边觉得遍历不合适一边想着反复序列化反序列化……序列化的开销和不优雅程度可比遍历大多了……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5088 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:41 · PVG 17:41 · LAX 02:41 · JFK 05:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.