Сопоставление объекта с массивом из нескольких объектов

#javascript #node.js #arrays #json

#javascript #node.js #массивы #json

Вопрос:

Мне нужно преобразовать это:

 {
 role:'admin',
 resource:['calendar','appraisal'],
 action:['create:any','read:any','update:any','delete:any']
}
{
 role:'user',
 resource:['calendar','appraisal'],
 action:['create:own','read:any','update:own','delete:own']
}
  

В это:

 [
 {role: 'admin', resource: 'calendar', action: 'create:any'},
 {role: 'admin', resource: 'calendar', action: 'read:any'},
 {role: 'admin', resource: 'calendar', action: 'update:any'},
 {role: 'admin', resource: 'calendar', action: 'delete:any'},
 {role: 'user', resource: 'calendar', action: 'create:own'},
 {role: 'user', resource: 'calendar', action: 'read:any'},
 {role: 'user', resource: 'calendar', action: 'update:own'},
 {role: 'user', resource: 'calendar', action: 'delete:own'},
]
  

Я пробовал по-разному с помощью метода Array.map(), но все равно безуспешно.
Я был бы признателен за любые ответы.
Спасибо!

Комментарии:

1. Почему это не appraisal учитывается? Должны ли мы принимать resource[0] во внимание только?

2. Ну нет, на самом деле оценка не будет принята во внимание, просто не написал ее, чтобы сделать фрагмент меньшего размера

Ответ №1:

Вы можете легко сделать это с помощью flatMap

 const data = [{
   role:'admin',
   resource:['calendar','appraisal'],
   action:['create:any','read:any','update:any','delete:any']
 },
 {
   role:'user',
   resource:['calendar','appraisal'],
   action:['create:own','read:any','update:own','delete:own']
}];

const mapped = data.flatMap(({role, resource, action}) =>
                 resource.flatMap(resource => 
                   action.map(action => ({role, resource, action}))));

console.log(mapped)  

Однако, поскольку это общее, это создаст также записи для appraisal . Если вы хотите пропустить их, вы можете легко сделать это после, с помощью фильтра:

 const calendars = mapped.filter(({resource}) => resource === "calendar");
  

Конечно, вы также можете получить оценки таким же образом. Этот подход также будет работать для любого другого элемента, который вы добавите в resource массив.

Комментарии:

1. Я рад узнать о методе flatMap … Я предполагаю, что альтернативой фильтрации позже является data.flatMap(({role, resource, action}) => action.map(action => ({role, resource: resource[0], action})))

2. Да, но это означает, что вы должны придерживаться первого значения resource массива: что, если порядок не согласован между элементами? Что, если «календарь» не является первым? Честно говоря, все еще можно фильтровать заранее, но для написания кода потребуется немного больше, и это будет иметь смысл, если элементов станет много.

3. Я продолжаю получать эту ошибку: flatMap does not exist on type {role: string, resource: string[], action:string[]}[]

4. @DiegoPlata выглядит так, как будто он был добавлен в ECMAScript 2019: tc39.es/ecma262/#sec-array.prototype.flatmap … таким образом, вы получите эту ошибку в более старой среде

Ответ №2:

Вы вызываете Array.flatMap() вызовы nest (последним может быть просто map) для итерации массивов ( resource , action ) и создания сплющенного массива объектов:

 const arr = [{
  role: 'admin',
  resource: ['calendar', 'appraisal'],
  action: ['create:any', 'read:any', 'update:any', 'delete:any']
}, {
  role: 'user',
  resource: ['calendar', 'appraisal'],
  action: ['create:own', 'read:any', 'update:own', 'delete:own']
}]

const result = arr.flatMap(({ resource: res, action: act, ...obj }) =>
  res.flatMap(resource =>
    act.map(action => ({
      ...obj,
      resource,
      action,
    }))
  )
)

console.log(result)  

Комментарии:

1. Не уверен, что это то, что требуется. Верно?

2. Кажется, это into this: показывает ожидаемый результат

3. Да, это то, что требуется, но я продолжаю получать эту ошибку: flatMap does not exist on type {role: string, resource: string[], action:string[]}[] и я действительно не знаю, почему

Ответ №3:

Я предполагаю, что ваш ввод представляет собой массив объектов (так что первый блок кода будет заключен в квадратные скобки). Либо вы создаете пустой массив в качестве накопителя и добавляете в него элементы по мере выполнения итерации по вводу, либо вы можете использовать [].concat(...) для получения вывода в виде одного оператора. Возможно, последнее более интересно в качестве ответа:

 flatten = arr => [].concat(...arr.map(obj =>
  obj.action.map(a =>
    ({
      role: obj.role,
      resource: obj.resource[0],
      action: a
    }))))

console.log(flatten([{
    role: 'admin',
    resource: ['calendar', 'appraisal'],
    action: ['create:any', 'read:any', 'update:any', 'delete:any']
  },
  {
    role: 'user',
    resource: ['calendar', 'appraisal'],
    action: ['create:own', 'read:any', 'update:own', 'delete:own']
  }
]))  

Это дает вам результат, который вы ищете, хотя, как отметил один из комментаторов, похоже, что вы игнорируете все, кроме первого элемента свойства ресурса. Было бы немного сложнее вывести список всех перестановок (их было бы 16), но может применяться та же стратегия. Это можно как-то обобщить и создать рекурсивную функцию, которая обрабатывает все случаи!

Ответ №4:

используя reduce, что-то вроде этого может работать

 const json = [
    {
        role:'admin',
        resource:['calendar','appraisal'],
        action:['create:any','read:any','update:any','delete:any']
   },
   {
        role: 'user',
        resource:['calendar','appraisal'],
        action:['create:own','read:any','update:own','delete:own']
    }  
];

const getOutput = (arr) => {
    return arr.reduce((acc, cur) => {
        const roleOutput = cur.action.reduce((actionAcc, actionCur) => 
            [
                ...actionAcc,
                ...cur.resource.map(resVal => ({
                    role: cur.role,
                    resource: resVal,
                    action: actionCur
                }))
            ]
        , []);
            
        return [...roleOutput, ...acc];
    }, []);
}

console.log(getOutput(json));