JavaScript сравнение массивов объектов

Помогите пожалуйста, бьюсь уже третий день.

У меня два массива в которых содержатся объекты.

// Массив №1. В нём содержаться все услуги и количество вложений услуг может быть любое
let allService = [
    {
        "Name": "1. Услуга №1",
        "Code": "1",
        "Items": {
            "Service": [
                {
                    "Name": "1.1 Услуга №1. шаг 1",
                    "Code": "8"
                },
                {
                    "Name": "1.2 Услуга №1. шаг 2",
                    "Code": "9"
                },
                {
                    "Name": "1.3 Услуга №1 шаг 3",
                    "Code": "10"
                }
            ]
        }
    },
    {
        "Name": "Услуга №2",
        "Code": "2"
    },
    {
        "Name": "Услуга №3",
        "Code": "3"
    },
    {
        "Name": "Услуга №4",
        "Code": "4"
    },
    {
        "Name": "Услуга №5",
        "Code": "5"
    },
    {
        "Name": "Услуга №6",
        "Code": "6"
    },
    {
        "Name": "7. Услуга 7",
        "Code": "7",
        "Items": {
            "Service": {
                "Name": "7.1 Услуга 7 шаг 1",
                "Code": "11",
                "Items": {
                    "Service": {
                        "Name": "7.1.1 Услуга 7.1.1",
                        "Code": "12"
                    }
                }
            }
        }
    }
]

// Массив №2. В нём содержаться МОИ услуги.
let myService = [
    {Service: '8'},
    {Service: '9'},
    {Service: '10'}
];

Подскажите, как мне из массива "allService" получить новый массив "notMyService" в котором будут все услуги за исключением тех что есть в массиве "myService". При условии, что значение Code из массива allService === значению Service из массива myService. Понимаю, что здесь нужна рекурсия, но никак не могу написать что-то более менее рабочее.

P.S. В идеале на выходе получить линейный структуру. Т.е. что-то типо такого

notMyService = [
    {Code: "2", Name: "Услуга №2"},
    {Code: "3", Name: "Услуга №3"},
    {Code: "4", Name: "Услуга №4"},
    {Code: "5", Name: "Услуга №5"},
    {Code: "6", Name: "Услуга №6"},
    {Code: "12", Name: "7.1.1 Услуга 7.1.1"},
]

Спасибо!


Ответы (2 шт):

Автор решения: Stepan Kasyanenko

Не универсальное решение, но решение.

let allService = [{
    "Name": "1. Услуга №1",
    "Code": "1",
    "Items": {
      "Service": [{
          "Name": "1.1 Услуга №1. шаг 1",
          "Code": "8"
        },
        {
          "Name": "1.2 Услуга №1. шаг 2",
          "Code": "9"
        },
        {
          "Name": "1.3 Услуга №1 шаг 3",
          "Code": "10"
        }
      ]
    }
  },
  {
    "Name": "Услуга №2",
    "Code": "2"
  },
  {
    "Name": "Услуга №3",
    "Code": "3"
  },
  {
    "Name": "Услуга №4",
    "Code": "4"
  },
  {
    "Name": "Услуга №5",
    "Code": "5"
  },
  {
    "Name": "Услуга №6",
    "Code": "6"
  },
  {
    "Name": "7. Услуга 7",
    "Code": "7",
    "Items": {
      "Service": {
        "Name": "7.1 Услуга 7 шаг 1",
        "Code": "11",
        "Items": {
          "Service": {
            "Name": "7.1.1 Услуга 7.1.1",
            "Code": "12"
          }
        }
      }
    }
  }
];

let myService = [{
    Service: '8'
  },
  {
    Service: '9'
  },
  {
    Service: '10'
  }
];

function flattenServices(services, servicesToExclude) {
  const result = [];
  services.forEach(service => {
    if (servicesToExclude.some(s => s.Service === service.Code)) {
      return;
    }
    result.push(service);
    const childService = service.Items?.Service;
    if (childService) {
      if (Array.isArray(childService)) {
        const resultChild = flattenServices(childService, servicesToExclude);
        result.push(...resultChild);
      } else if (typeof childService === 'object') {
        const resultChild = flattenServices([childService], servicesToExclude);
        result.push(...resultChild);
      }
    }
  });
  return result;
}

const flatten = flattenServices(allService, myService);
console.log(flatten);

→ Ссылка
Автор решения: Grundy

В итоге задачу можно свести к поиску листовых узлов (узлов без дочерних элементов) и выборке из них тех, которые подходят под условия.

Для поиска листовых узлов удобно воспользоваться функцией-генератором

function* lists(tree){
  for(const node of tree){
    if (!node.Items?.Service) yield node; // если нет детей узел листовой - возвращаем его
    
    else yield* lists([].concat(node.Items.Service)); // иначе спускаемся на уровень вниз
  }
}

Далее, достаточно просто отфильтровать полученный список узлов, например, с помощью той же функции-генератора

function* filter(nodes, exclude){
  for(const node of nodes){ // бежим по коллекции
    if(!exclude.some(e => e.Service == node.Code)) yield node; // если узел не надо исключать - возвращаем его
  }
}

В итоге код может выглядеть так:

let allService = [
    {
        "Name": "1. Услуга №1",
        "Code": "1",
        "Items": {
            "Service": [
                {
                    "Name": "1.1 Услуга №1. шаг 1",
                    "Code": "8"
                },
                {
                    "Name": "1.2 Услуга №1. шаг 2",
                    "Code": "9"
                },
                {
                    "Name": "1.3 Услуга №1 шаг 3",
                    "Code": "10"
                }
            ]
        }
    },
    {
        "Name": "Услуга №2",
        "Code": "2"
    },
    {
        "Name": "Услуга №3",
        "Code": "3"
    },
    {
        "Name": "Услуга №4",
        "Code": "4"
    },
    {
        "Name": "Услуга №5",
        "Code": "5"
    },
    {
        "Name": "Услуга №6",
        "Code": "6"
    },
    {
        "Name": "7. Услуга 7",
        "Code": "7",
        "Items": {
            "Service": {
                "Name": "7.1 Услуга 7 шаг 1",
                "Code": "11",
                "Items": {
                    "Service": {
                        "Name": "7.1.1 Услуга 7.1.1",
                        "Code": "12"
                    }
                }
            }
        }
    }
]

// Массив №2. В нём содержаться МОИ услуги.
let myService = [
    {Service: '8'},
    {Service: '9'},
    {Service: '10'}
];

function* lists(tree){
  for(const node of tree){
    if (!node.Items?.Service) yield node;
    
    else yield* lists([].concat(node.Items.Service));
  }
}

function* filter(nodes, exclude){
  for(const node of nodes){
    if(!exclude.some(e => e.Service == node.Code)) yield node;
  }
}

console.log([...filter(lists(allService), myService)])

→ Ссылка