【问题标题】:Merge two arrays connected by an intermediate array with Ramda用 Ramda 合并由中间数组连接的两个数组
【发布时间】:2018-07-04 19:32:03
【问题描述】:

我最近开始使用 Ramda 来处理来自 JSONAPI 的响应。我在处理复杂的关系和想办法从多个对象中获取我想要的数据时遇到了一些麻烦。

一个用户被分配给一个user_role,它被分配给一个角色。一个角色可以有多个 user_role,但一个 user_role 只能分配给一个角色。一个用户可以有多个 user_role,但为了简单起见,我只为每个用户分配了一个 user_role。

我的目标是获取 user_role 中引用的角色并将其放置在用户对象中的新“包含”对象中。

例如:

取这三组数据,用户,user_roles,角色:

const users = [
   {   
     id: 1, 
     attributes: {
       firstName: "Bob",
       lastName: "Lee"
     }, 
     relationships: {
       user_roles: {
         data: {
           id: 1, 
           type: "user_roles"
         }
       }
     },
     type: "users"
   },
   {   
     id: 2, 
     attributes: {
       firstName: "Kevin",
       lastName: "Smith"
     }, 
     relationships: {
       user_role: {
          data: {
            id: 2, 
            type: "user_roles"
          }
       }
     },
     type: "users"
   },
 ];

 const user_roles = [
   {
     id: 1,
     attributes: {
       createdAt: "7/3/2018",
       updatedAt: "7/3/2018"
     },
     relationships: {
       role: {
         data: {
           id: 3,
           type: "roles"
         }
       }
     },
     type: "user_roles"
   },
   {
     id: 2,
     attributes: {
       createdAt: "7/1/2018",
       updatedAt: "7/1/2018"
     },
     relationships: {
       role: {
         data: {
           id: 4,
           type: "roles"
         }
       }
     },
     type: "user_roles"
   } 
 ]

 const roles = [
   {  
     id: 3,
     attributes: {
       name: "manager",
       description: "manages stuff"
     },
     relationships: {
       user_roles: {
         data: [
           { 
             id: 1,
             type: "user_roles"
           },
           { 
             id: 10,
             type: "user_roles"
           }
         ]
       } 
     },
     type: "roles"
   },
   {   
     id: 4,
     attributes: {
       name: "director",
       description: "directs stuff"
     },
     relationships: {
       user_roles: {
         data: [
           { 
             id: 2,
             type: "user_roles"
           }
         ]
       } 
     },
     type: "roles"
   },
 ]

我需要的是一个如下所示的用户对象:

const newUser = [
   {   
     id: 1, 
     attributes: {
       firstName: "Bob",
       lastName: "Lee"
     }, 
     relationships: {
       user_roles: {
         data: {
         id: 1, 
           type: "user_roles"
         }
       }
     },
     type: "users",
     included: [
        {
          role: {
            name: "manager",
            description: "manages stuff"
          }
        }
     ]
   },
   {   
     id: 2, 
     attributes: {
       firstName: "Kevin",
       lastName: "Smith"
     }, 
     relationships: {
       user_role: {
         data: {
           id: 2, 
           type: "user_roles"
         }
       }
    },
    type: "users",
      included: [
        { 
          role: {
            name: "director",
            description: "directs stuff"
          }
        }
      ]
    }, 
  ];

我学会了如何将两个数组合并在一起,但是拥有这个“中间”数组真的让我很反感,我什至不知道从哪里开始!

【问题讨论】:

    标签: javascript arrays object ramda.js


    【解决方案1】:

    我的建议是将这些部分分解为单独的函数,然后将它们组合在一起。

    注意:在下面的示例中,我已将嵌套在用户对象中的 user_roles 属性更新为用户角色数组,如您的描述中所建议的那样。

    首先,如果id 经常查找这些项目,我建议创建这些列表的索引版本

    const rolesIdx = R.indexBy(R.prop('id'), roles)
    const userRolesIdx = R.indexBy(R.prop('id'), user_roles)
    

    然后我们可以创建一个函数管道,当给定user_role 对象时,这些函数将创建最终将出现在included 数组中的元素的所需形状。

    const attributesForUserRole = R.pipe(
      R.path(['data', 'id']),
      R.flip(R.prop)(userRolesIdx),
      R.path(['relationships', 'role', 'data', 'id']),
      R.flip(R.prop)(rolesIdx),
      R.prop('attributes'),
      R.objOf('role')
    )
    

    然后我们可以创建一个函数,该函数将使用上述attributesForUserRole 函数将角色列表添加到included 属性中。

    const addIncludedRoles = user =>
      R.assoc(
        'included',
        R.map(attributesForUserRole, user.relationships.user_roles),
        user
      )
    

    这也可以用无点形式重写,尽管这可能会降低可读性(由您决定)。

    const addIncludedRoles = R.chain(
      R.assoc('included'),
      R.o(R.map(attributesForUserRole), R.path(['relationships', 'user_roles']))
    )
    

    此时,只需使用addIncludedRoles 函数映射您的users 列表即可。

    R.map(addIncludedRoles, users)
    

    一起来:

    const users = [
       {   
         id: 1, 
         attributes: {
           firstName: "Bob",
           lastName: "Lee"
         }, 
         relationships: {
           user_roles: [{
             data: {
               id: 1, 
               type: "user_roles"
             }
           }]
         },
         type: "users"
       },
       {   
         id: 2, 
         attributes: {
           firstName: "Kevin",
           lastName: "Smith"
         }, 
         relationships: {
           user_roles: [{
              data: {
                id: 2, 
                type: "user_roles"
              }
           }]
         },
         type: "users"
       },
     ];
    
     const user_roles = [
       {
         id: 1,
         attributes: {
           createdAt: "7/3/2018",
           updatedAt: "7/3/2018"
         },
         relationships: {
           role: {
             data: {
               id: 3,
               type: "roles"
             }
           }
         },
         type: "user_roles"
       },
       {
         id: 2,
         attributes: {
           createdAt: "7/1/2018",
           updatedAt: "7/1/2018"
         },
         relationships: {
           role: {
             data: {
               id: 4,
               type: "roles"
             }
           }
         },
         type: "user_roles"
       } 
     ]
    
     const roles = [
       {  
         id: 3,
         attributes: {
           name: "manager",
           description: "manages stuff"
         },
         relationships: {
           user_roles: {
             data: [
               { 
                 id: 1,
                 type: "user_roles"
               },
               { 
                 id: 10,
                 type: "user_roles"
               }
             ]
           } 
         },
         type: "roles"
       },
       {   
         id: 4,
         attributes: {
           name: "director",
           description: "directs stuff"
         },
         relationships: {
           user_roles: {
             data: [
               { 
                 id: 2,
                 type: "user_roles"
               }
             ]
           } 
         },
         type: "roles"
       },
     ]
    
    const rolesIdx = R.indexBy(R.prop('id'), roles)
    const userRolesIdx = R.indexBy(R.prop('id'), user_roles)
    
    const attributesForUserRole = R.pipe(
      R.path(['data', 'id']),
      R.flip(R.prop)(userRolesIdx),
      R.path(['relationships', 'role', 'data', 'id']),
      R.flip(R.prop)(rolesIdx),
      R.prop('attributes'),
      R.objOf('role')
    )
    
    const addIncludedRoles = user =>
      R.assoc(
        'included',
        R.map(attributesForUserRole, user.relationships.user_roles),
        user
      )
    
    const result = R.map(addIncludedRoles, users)
    
    console.log(result)
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

    【讨论】:

    • 感谢您的深入回答!但是,user_roles 实际上是一个包含数据数组的对象。像这样: user_roles: { data: [ { id: 2, type: "user_roles" }, { id: 3, type: "user_roles" }, ] }
    • 所以插入的角色对我来说显示为“未定义”。我尝试在这一步更深一层:R.map(attributesForUserRole, user.relationships.user_roles.data) 但我收到此错误“无法读取未定义的属性 'fantasy-land/map'”
    • 没关系,明白了!我使用条件语句仅在具有该数据的情况下才走该路线。由于用户没有分配角色的情况,我似乎收到了该错误。
    【解决方案2】:

    大合并:

    为了解决这个问题,我创建了以下内容:

    users.concat(user_roles).concat(roles).reduce((newArray, obj, _, arr) => newArray.find(obj2 => obj2.id === obj.id) ? newArray : newArray.concat(R.mergeAll(arr.filter(o => o.id === obj.id))), [])
    

    我不确定这是否能满足您的需求,但这就是它的工作方式:

    1. 将所有数组连接在一起;我们就叫它joinedArray
    2. 创建一个新数组; newArray
    3. 迭代joinedArray
      1. If 当前的id 存在于newArray 中不要向其推送任何内容。
      2. Else 使用 .filter 获取所有 id 并使用 R.mergeAll 合并所有这些。

    【讨论】:

      【解决方案3】:

      您可以使用函数式编程来解决您的问题,使用数组的 map 方法作为休闲方式:

      var newUser = users.map(function(user){
          var _user_role = user_roles.find(function(user_role){
              // here you get the user_role asociated to a user.
              return user_role.id === user.relationships.user_roles.data.id
          })
          var _role = roles.find(function(role){
              // here you get the role asociated to a user_role
              return role.id === _user_role.relationships.role.data.id
          })
          return {
              id: user.id,
              attributes: user.attributes,
              relationships: user.relationships,
              type: user.type,
              included: [{
                  role: _role.attributes
              }]
          }
      })
      

      可能还有更多优化,我尽可能简单地让您理解。

      【讨论】:

      • 第一个数组(用户)也有错误,在第二个索引上,名为“user_role”的“relationships”属性与第一个数组的“user_roles”不匹配,如果您使用这些数组作为参考,可能会带来错误。
      猜你喜欢
      • 2018-12-15
      • 1970-01-01
      • 2013-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      相关资源
      最近更新 更多