1

I have an array named A as following:

  {
    name: 'activityRelatedManager',
    meta: {
      title: 'Activity Related Manager',
      permissions: []
    },
    children: [
      {
        name: 'activityPublish',
        meta: { title: 'Publish Activity', permissions:[] }
      },
      {
        name: 'activityManager',
        meta: { title: 'Manage Activity', permissions:[] }
      },
      {
        name: 'activityTypeManager',
        meta: { title: ' Manage Activity Type', permissions:[] }
      },
      {
        name: 'activityLocationManager',
        meta: { title: ' Manage Activity Location', permissions:[] }
      }
    ]
  },

I also have an array named B like this:

[
    {
      "id": 1,
      "name": "activityRelatedManager",
      "permissions": [
        "activity:publish"
      ]
    },
    {
      "id": 2,
      "name": "activityManager",
      "permissions": [
        "activity:index",
        "activity:show"
      ]
    },
    {
      "id": 3,
      "name": "activityTypeManager",
      "permissions": []
    }
]

What i want to do is to find the duplicated values based on 'name' key in B, and then assign the permissions value to A. The result should be something like this:

  {
    name: 'activityRelatedManager',
    meta: {
      title: 'Activity Related Manager',
      permissions: ["activity:publish"]
    },
    children: [
      {
        name: 'activityManager',
        meta: { title: 'Manage Activity', permissions:["activity:index", "activity:show"] }
      },
      {
        name: 'activityTypeManager',
        meta: { title: ' Manage Activity Type', permissions:[] }
      }
    ]
  },

I think the assignment is easy, but how to find the duplication?

  A.map(ele => {
    if (!ele.name || (!ele.meta && !ele.meta.permissions)) return
    for (let i = 0; i < B.length; i++) {
      const element = B[i]
      if (ele.name === element.name) {
        ele.meta.permissions = element.permissions
      }
    }
    if (ele.children) {
      makePermissionRouters(B, A.children)
    }
  })
  return clientAsyncRoutes

I'd appreciate it if someone can help me solve this question.

6
  • Do you want your output to keep only those entries in A which have a match in B? That's what it looks like because your sample output does not include activityLocationManager. But if so, what do you want to happen if the parent node of an included node would not be included? That is, what would happen if activityRelatedManager had no match in B? Commented Apr 15, 2020 at 10:58
  • @ScottSauyet The root node which in this example is 'activityRelatedManager ' always exists in the B array. Commented Apr 15, 2020 at 11:07
  • But you're asking about this recursively. What would happen if activityLocationManager had children that had a match in B? You don't include it now. Would you include it then? Commented Apr 15, 2020 at 11:14
  • @ScottSauyet Nope, the activityLocationManager will not include the children that will have a match in B. Commented Apr 15, 2020 at 11:18
  • should that read "the children that do not have a match in B"? Else I'm confused. Commented Apr 15, 2020 at 11:22

1 Answer 1

1

I would break out a utility function here, a deep version of a filterMap function, which takes a predicate and a transformation function and recursively filters out every level of children according to the predicate and then transforms the remaining ones with the supplied function.

You can use this by first reformatting B to be a mapping -- here we call it perms -- between names and lists of permissions, then calling this deepFilterMap with a predicate that checks if the object's name is in perms and a transform that sets meta.permissions to that value.

This version of deepFilterMap is specific to structures where the descendant nodes are in an array called children. We could make it more generic, but that would involve another parameter to the function. It's an interesting exercise, but perhaps overkill here.

const deepFilterMap = (pred, transform) => (tree) =>
  pred (tree)
    ? {
        ... transform (tree), 
        ... (tree.children 
              ? {children: tree .children 
                                .filter (pred) 
                                .map (deepFilterMap (pred, transform))
                } 
              : {}
            )
      }
    : {}

const transform = (A, B) => {
  const perms = Object .fromEntries (B .map (
    ({name, permissions}) => [name, permissions])
  )

  return deepFilterMap (
    ({name}) => name in perms, 
    ({name, meta, ...rest}) => ({
      name, 
      meta: {...meta, permissions: perms [name]}, 
      ...rest
    })
  ) (A)
}

const A = {name: "activityRelatedManager", meta: {title: "Activity Related Manager", permissions: []}, children: [{name: "activityPublish", meta: {title: "Publish Activity", permissions: []}}, {name: "activityManager", meta: {title: "Manage Activity", permissions: []}}, {name: "activityTypeManager", meta: {title: " Manage Activity Type", permissions: []}}, {name: "activityLocationManager", meta: {title: " Manage Activity Location", permissions: []}}]};
const B = [{id: 1, name: "activityRelatedManager", permissions: ["activity: publish"]}, {id: 2, name: "activityManager", permissions: ["activity: index", "activity: show"]}, {id: 3, name: "activityTypeManager", permissions: []}];

console .log (transform (A, B))
.as-console-wrapper {min-height: 100% !important; top: 0}

We could, of course, inline the work of deepFilterMap into our main function. Overall, we would have fewer lines of code. But to my mind, breaking down the complexity in this manner is much more maintainable. And you have a potentially reusable function available now.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.