Объединить два массива, соединенных промежуточным массивом с Рамдой - PullRequest
0 голосов
/ 04 июля 2018

Я недавно начал использовать 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 голосов
/ 04 июля 2018

Вы можете использовать функциональное программирование для решения вашей проблемы, используя метод 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
        }]
    }
})

Возможна еще некоторая оптимизация, я сделал ее максимально простой для вашего понимания.

0 голосов
/ 05 июля 2018

Я бы предложил разделить части на отдельные функции, а затем объединить их вместе.

Примечание: в приведенном ниже примере я обновил свойство 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>
0 голосов
/ 04 июля 2018

Большое слияние:

В попытке решить эту проблему я создал следующее:

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, чтобы объединить их все.
...