将两个由中间阵列连接的阵列与Ramda合并

时间:2018-07-04 19:32:03

标签: javascript arrays object ramda.js

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

用户被分配给user_role,后者被分配给角色。一个角色可以有多个user_roles,但是一个user_role仅分配给一个角色。一个用户可以有多个user_roles,但是为简单起见,我只为每个用户分配了一个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"
          }
        }
      ]
    }, 
  ];

我学会了如何将两个数组合并在一起,但是拥有这个“中间”数组确实让我大吃一惊,甚至在弄清楚从哪里开始时我也遇到了麻烦!

3 个答案:

答案 0 :(得分:1)

我的建议是将各部分分解为单独的功能,然后重新组合在一起。

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

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

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

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

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']))
)

这时,只需使用users函数映射到addIncludedRoles列表即可。

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>

答案 1 :(得分:-1)

大合并:

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

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合并所有它们。

答案 2 :(得分:-1)

您可以使用函数式编程来解决您的问题,将map的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
        }]
    }
})

可能还有更多优化,为了您的理解,我将其简化了。