基于ID键从数组中更新或从数组中删除的对象数组的JavaScript递归函数

发布于 2025-01-22 04:27:44 字数 4508 浏览 0 评论 0原文

我试图在下面的一系列对象上创建两个(2)递归功能。我认为这两个功能是“相似的”,但它们做了两种不同的事情。

函数1应该更新对象 - 可能是“找到”对象中的每个字段并返回对象的“新”数组,因此该功能需要通过.id

code 2需要通过.id识别适当的对象,但要删除该对象,然后再次返回对象的“新”数组。

我已经尝试了多种方法(在对象的数组以下) - 但是,我无济于事,我无法让新对象返回。

要注意即使每个对象都有变化/不同的键,总会有一个.id -

[
    {
        "type":"section",
        "name":"Section 1",
        "hassection":[      
            {
                "type":"section",
                "id":1,
                "name":"Section 1 child section 1",
                "hasMenuItem":
                    [
                        {
                            "type":"item",
                            "id":2,
                            "name":"Item 1",
                            "prices":
                            {
                                "type":"price",
                                "price":"15.95"
                            },
                            "description":"Blah Blah..."
                        },{
                            "type":"item",
                            "id":3,"name":
                            "Item 2",
                            "prices":[
                                {
                                    "type":"price",
                                    "price":"24.95"
                                },{
                                    "type":"price",
                                    "price":"13.95"
                                }
                            ],
                            "description":"Blah Blah..."
                        }
                ]
            },{
                "type":"section",
                "id":4,
                "name":"Section 1 child section 2",
                "hasitem":[
                    {
                        "type":"item",
                        "name":"Item 3",
                        "prices":
                        {
                            "type":"price","price":"14.50"
                        },
                        "description":"Blah Blah..."
                    },{
                        "type":"item",
                        "id":5,
                        "name":"Item 4",
                        "prices":
                        {
                            "type":"price",
                            "price":"14.50"
                        },
                        "description":"Blah Blah..."
                    }
                ]
            },
        ]},{
            "type":"section",
            "name":"Section 2",
            "hassection":[      
                {
                    "type":"section",
                    "id":6,
                    "name":"Section 2 child section 1",
                    "hasitem":[
                        {
                            "type":"item",
                            "id":7,
                            "name":"Item 5",
                            "prices":
                            {
                                "type":"price",
                                "price":"15.95"
                            },
                            "description":"Blah Blah..."
                        },
                        {
                            "type":"item",
                            "id":8,
                            "name":"Item 6",
                            "prices":
                            {
                                "type":"price",
                                "price":"13.95"
                            },
                            "description":"Blah Blah..."
                        }
                    ]
            }
        ]}
    ]

我的更新函数

function updateNestedObj(obj,updates) {
    const updateToApply = updates.find(upd => upd.id === obj.id);
    if (updateToApply) {
        // UPDATE THE OBJ
    }
    
    for(let k in obj) {
        if (typeof(obj[k]) === 'object') {
            // LOOP THROUGH THE OBJECT
            updateNestedObj(obj[k], updates);
        }
    }
    return updateToApply
}

我的删除函数,

function deleteNestedObj(obj, updates) {
    const updateToApply = updates.find(upd => upd.id === obj.id);
    if (updateToApply) {
        delete upd;       
    }    
    for(let k in obj) {
        if (typeof(obj[k]) === 'object') {
            deleteNestedObj(obj[k], updates);
        }
    }
}

我只是无法理解如何“工作”他们” - 事先感谢,任何帮助都非常感谢。

I'm trying to create two (2) recursive functions to "loop" over an array of objects like below. I think the two functions are "similar" but they do two different things.

Function 1 should update the object - which could be every field in the "found" object and return the "new" array of objects, so the function needs to identify the appropriate object by .id

Function 2 needs to identify the appropriate object by .id BUT to delete that object and again return the "new" array of objects.

I've tried a number of ways (below the array of objects) - but to no avail, I cannot get the new object to return.

To note even if each object has varying/different keys, there will always be an .id key -

[
    {
        "type":"section",
        "name":"Section 1",
        "hassection":[      
            {
                "type":"section",
                "id":1,
                "name":"Section 1 child section 1",
                "hasMenuItem":
                    [
                        {
                            "type":"item",
                            "id":2,
                            "name":"Item 1",
                            "prices":
                            {
                                "type":"price",
                                "price":"15.95"
                            },
                            "description":"Blah Blah..."
                        },{
                            "type":"item",
                            "id":3,"name":
                            "Item 2",
                            "prices":[
                                {
                                    "type":"price",
                                    "price":"24.95"
                                },{
                                    "type":"price",
                                    "price":"13.95"
                                }
                            ],
                            "description":"Blah Blah..."
                        }
                ]
            },{
                "type":"section",
                "id":4,
                "name":"Section 1 child section 2",
                "hasitem":[
                    {
                        "type":"item",
                        "name":"Item 3",
                        "prices":
                        {
                            "type":"price","price":"14.50"
                        },
                        "description":"Blah Blah..."
                    },{
                        "type":"item",
                        "id":5,
                        "name":"Item 4",
                        "prices":
                        {
                            "type":"price",
                            "price":"14.50"
                        },
                        "description":"Blah Blah..."
                    }
                ]
            },
        ]},{
            "type":"section",
            "name":"Section 2",
            "hassection":[      
                {
                    "type":"section",
                    "id":6,
                    "name":"Section 2 child section 1",
                    "hasitem":[
                        {
                            "type":"item",
                            "id":7,
                            "name":"Item 5",
                            "prices":
                            {
                                "type":"price",
                                "price":"15.95"
                            },
                            "description":"Blah Blah..."
                        },
                        {
                            "type":"item",
                            "id":8,
                            "name":"Item 6",
                            "prices":
                            {
                                "type":"price",
                                "price":"13.95"
                            },
                            "description":"Blah Blah..."
                        }
                    ]
            }
        ]}
    ]

My update function

function updateNestedObj(obj,updates) {
    const updateToApply = updates.find(upd => upd.id === obj.id);
    if (updateToApply) {
        // UPDATE THE OBJ
    }
    
    for(let k in obj) {
        if (typeof(obj[k]) === 'object') {
            // LOOP THROUGH THE OBJECT
            updateNestedObj(obj[k], updates);
        }
    }
    return updateToApply
}

My Delete function

function deleteNestedObj(obj, updates) {
    const updateToApply = updates.find(upd => upd.id === obj.id);
    if (updateToApply) {
        delete upd;       
    }    
    for(let k in obj) {
        if (typeof(obj[k]) === 'object') {
            deleteNestedObj(obj[k], updates);
        }
    }
}

I just cannot fathom out how to "work them" - thanks in advance, any help much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

美羊羊 2025-01-29 04:27:44

generics

让我们从不变的更新(t,func)开始,该采用任何类型的值,t和可呼出的更新器功能,<代码> func 。 func根据呼叫者的指定返回值转换t。如果没有返回值,即undefined,那么update will remove 该值从树中 -

function update(t, func) {
  switch (t?.constructor) {
    case Object:
      return Object.entries(t).reduce((r, [k, v]) => {
        const newValue = update(func(v), func)
        if (newValue !== undefined) r[k] = newValue
        return r
      }, {})
    case Array:
      return t.flatMap(v => {
        const newValue = update(func(v), func)
        return newValue === undefined ? [] : [newValue]
      })
    default:
      return t
  }
}

不成熟的remove(t,func) 的专业化

function remove(t, func) {
  return update(t, v => Boolean(func(v)) ? undefined : v)
}

可以定义为更新 - 特殊表格

该功能可以进一步专业化以满足您的特定需求。 updateWithObj(t,obj)将递归更新t其中一个节点的id匹配obj.id obj.id -

function updateWithObj(t, obj) {
  return update(t, v => v.id == obj.id ? {...v, ...obj} : v)
}

同样。 > removewithObj(t,obj)递归从t中删除,其中节点的id匹配obj.id -

function removeWithObj(t, obj) {
  return remove(t, v => v.id == obj.id)
}

示例< /strong>

让我们创建一些示例数据。对于它的价值,Update不在乎它是元素数组,[...]或一个对象,{...} -

const data = [
  {id: 1, data: 50 },
  {id: 2, data: {id: 3, data: "foo"}},
  {id: 4, data: [{id: 5, data: 3.141}, {id: 6, data: {id: 7, data: "bar"}}]}
]

我们将从obj.id == 1上的简单更新开始。请注意,现有的数据属性保留在触觉中,并添加了新的ok属性。所有其他节点保持不变 -

console.log(updateWithObj(data, {id: 1, ok: "✅"}))
[
{
"id": 1,
"data": 50, //

generics

Let's start with a immutable update(t, func) that takes a value of any type, t, and a callable updater function, func. func transforms t per the caller's specified return value. If no value is returned, ie undefined, then update will remove that value from the tree -

function update(t, func) {
  switch (t?.constructor) {
    case Object:
      return Object.entries(t).reduce((r, [k, v]) => {
        const newValue = update(func(v), func)
        if (newValue !== undefined) r[k] = newValue
        return r
      }, {})
    case Array:
      return t.flatMap(v => {
        const newValue = update(func(v), func)
        return newValue === undefined ? [] : [newValue]
      })
    default:
      return t
  }
}

Immutable remove(t, func) can be defined as a specialization of update -

function remove(t, func) {
  return update(t, v => Boolean(func(v)) ? undefined : v)
}

special forms

The functions can be further specialized to match your particular needs. updateWithObj(t, obj) will recursively update t where a node's id matches obj.id -

function updateWithObj(t, obj) {
  return update(t, v => v.id == obj.id ? {...v, ...obj} : v)
}

Likewise removeWithObj(t, obj) recursively removes from t where a node's id matches obj.id -

function removeWithObj(t, obj) {
  return remove(t, v => v.id == obj.id)
}

examples

Let's create some sample data. For what it's worth, update doesn't care whether it is an array of elements, [...] or a single object, {...} -

const data = [
  {id: 1, data: 50 },
  {id: 2, data: {id: 3, data: "foo"}},
  {id: 4, data: [{id: 5, data: 3.141}, {id: 6, data: {id: 7, data: "bar"}}]}
]

We'll start with a simple update on obj.id == 1. Note the existing data attribute remains in tact and a new ok attribute is added. All other nodes remain unchanged -

console.log(updateWithObj(data, {id: 1, ok: "✅"}))
[
  {
    "id": 1,
    "data": 50,  // ???????? remains unchanged
    "ok": "✅"  // ???????? updated
  },
  {
    "id": 2,
    "data": {
      "id": 3,
      "data": "foo"
    }
  },
  {
    "id": 4,
    "data": [
      {
        "id": 5,
        "data": 3.141
      },
      {
        "id": 6,
        "data": {
          "id": 7,
          "data": "bar"
        }
      }
    ]
  }
]

Here we see a deeply nested update with obj.id == 7. Note the data attribute for this node is updated and a new ok attribute is added -

console.log(updateWithObj(data, {id: 7, data: 0.123, ok: "✅"}))
[
  {
    "id": 1,
    "data": 50
  },
  {
    "id": 2,
    "data": {
      "id": 3,
      "data": "foo"
    }
  },
  {
    "id": 4,
    "data": [
      {
        "id": 5,
        "data": 3.141
      },
      {
        "id": 6,
        "data": {
          "id": 7,
          "data": 0.123, // ???????? updated
          "ok": "✅"     // ???????? updated
        }
      }
    ]
  }
]

Now let's see removal using removeWithObj. Notice obj.id == 6 is removed along with its descendants -

console.log(removeWithObj(data, {id: 6}))
[
  {
    "id": 1,
    "data": 50
  },
  {
    "id": 2,
    "data": {
      "id": 3,
      "data": "foo"
    }
  },
  {
    "id": 4,
    "data": [
      {
        "id": 5,
        "data": 3.141
      }
      // ???????? node removed
    ]
  }
]

live demo

Here's a demo you can run in your own browser -

function update(t, func) {
  switch (t?.constructor) {
    case Object:
      return Object.entries(t).reduce((r, [k, v]) => {
        const newValue = update(func(v), func)
        if (newValue !== undefined) r[k] = newValue
        return r
      }, {})
    case Array:
      return t.flatMap(v => {
        const newValue = update(func(v), func)
        return newValue === undefined ? [] : [newValue]
      })
    default:
      return t
  }
}

function remove(t, func) {
  return update(t, v => Boolean(func(v)) ? undefined : v)
}

function updateWithObj(t, obj) {
  return update(t, v => v.id == obj.id ? {...v, ...obj} : v)
}

function removeWithObj(t, obj) {
  return remove(t, v => v.id == obj.id)
}

const data = [
  {id: 1, data: 50 },
  {id: 2, data: {id: 3, data: "foo"}},
  {id: 4, data: [{id: 5, data: 3.141}, {id: 6, data: {id: 7, data: "bar"}}]}
]

console.log(updateWithObj(data, {id: 1, ok: "✅"}))
console.log(updateWithObj(data, {id: 7, data: 0.123, ok: "✅"}))
console.log(removeWithObj(data, {id: 6}))
.as-console-wrapper { min-height: 100%; top: 0; }

why undefined?

@Scott's comment draws attention to use of undefined as the mechanism for removal. I have always advocated for the programmer to reserve the right to use undefined for her/his particular needs. If a user gives undefined to a program, they can expect undefined behavior. For update, the undefined value is explicitly used to make a value not defined, or not there, ie remove it.

Other reasons support this choice. In most cases, an object with an explicitly undefined key behaves the same as one without the key. If the user really wants a var/key to be present but not yet set to a value, this is the perfect use of null -

const a = { foo: undefined }  // foo is defined, but also not defined ??
const b = {}                  // does not have foo
console.log(a.foo, b.foo)     // same behavior
// undefined undefined

JSON considers an undefined as "not defined" and so removes it when serializing an object -

const o = { a: undefined, b: null, c: false, d: 0, e: "" }
const j = JSON.stringify(o)

// "a" is not defined, so it's not serialized
console.log(j)

// looking for "a"? it's not defined :D
console.log(JSON.parse(j).a)

// undefined input gives undefined output. it's not defined :D
console.log(JSON.stringify(undefined))

ReScript plainly encodes None (ie "no value") as undefined in its Option module. Any Some(value) is represented as value -

// rescript
let foo = Some(1)

switch foo {
  | Some(z) => Js.log(z)
  | None => Js.log("no value")
}
// compiled javascript
var foo = 1;

if (foo !== undefined) {
  console.log(foo);
} else {
  console.log("no value");
}

explicit symbol

Maybe none of that convinces you and your fragile program still depends on having that undefined appear in the output. An explicit none sentinel can be used to signal to update that a particular value should be removed -

const none = Symbol() // ✅ removal sentinel
function update(t, func) {
  switch (t?.constructor) {
    case Object:
      return Object.entries(t).reduce((r, [k, v]) => {
        const newValue = update(func(v), func)
        if (newValue !== none) r[k] = newValue // ✅
        return r
      }, {})
    case Array:
      return t.flatMap(v => {
        const newValue = update(func(v), func)
        return newValue === none ? [] : [newValue] // ✅
      })
    default:
      return t
  }
}
function remove(t, func) {
  return update(t, v => Boolean(func(v)) ? none : v) // ✅
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文