Как можно использовать сокращение карты в javascript для суммирования информации в многослойном вложенном объекте? - PullRequest
0 голосов
/ 10 февраля 2020

Я пытаюсь написать функцию, которая будет эффективно суммировать данные из серии вложенных javascript объектов. Я полагаю, что это должно быть выполнимо с помощью метода Reduce, но я не очень опытен в использовании Reduce, и почти все примеры, которые я могу найти, используют вложенные массивы или некоторую комбинацию массивов и объектов, в то время как мои данные поступают исключительно как последовательность вложенных объектов (и является более глубоким, чем примеры, которые я могу найти.

Вот мои необработанные данные:

var activity = {
    "Network": {
      "ID": "Network",
      "MLS": {
        "ID": "MLS",
        "Ports": {
          "ID": "Ports",
          "GigabitEthernet0/1": {
            "ID": "GigabitEthernet0/1",
            "Port Mode": {
              "ID": "Port Mode",
              "Name": "Port Mode",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            },
            "Native VLAN": {
              "ID": "Native VLAN",
              "Name": "Native VLAN",
              "Value": "99",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "GigabitEthernet0/2": {
            "ID": "GigabitEthernet0/2",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "209.165.200.225",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "Subnet Mask": {
              "ID": "Subnet Mask",
              "Name": "Subnet Mask",
              "Value": "255.255.255.252",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:A::1": {
                "ID": "2001:DB8:ACAD:A::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:A::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            },
            "SwitchPort": {
              "ID": "SwitchPort",
              "Name": "SwitchPort",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            }
          },
          "Vlan10": {
            "ID": "Vlan10",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.10.254",
              "PointsPossible": 2,
              "PointsEarned": 2,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:10::1": {
                "ID": "2001:DB8:ACAD:10::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:10::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan20": {
            "ID": "Vlan20",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.20.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:20::1": {
                "ID": "2001:DB8:ACAD:20::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:20::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan30": {
            "ID": "Vlan30",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.30.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:30::1": {
                "ID": "2001:DB8:ACAD:30::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:30::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan99": {
            "ID": "Vlan99",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.99.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            }
          }
        },
        "Routes": {
          "ID": "Routes",
          "Static Routes": {
            "ID": "Static RoutesV2",
            "Name": "Static Routes",
            "Value": "",
            "PointsPossible": 0,
            "PointsEarned": 0,
            "ComparatorClass": 1
          },
          "IP Routing": {
            "ID": "IP Routing",
            "Name": "IP Routing",
            "Value": "1",
            "PointsPossible": 1,
            "PointsEarned": 1,
            "ComparatorClass": 1
          }
        },
        "Routesv6": {
          "ID": "Routesv6",
          "IPv6 Unicast Routing": {
            "ID": "Ipv6 Unicast Routing",
            "Name": "IPv6 Unicast Routing",
            "Value": "1",
            "PointsPossible": 1,
            "PointsEarned": 1,
            "ComparatorClass": 1
          }
        },
        "VLANS": {
          "ID": "VLANS",
          "VLAN 10": {
            "ID": "10",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Staff",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "VLAN 20": {
            "ID": "20",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Student",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "VLAN 30": {
            "ID": "30",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Faculty",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          }
        }
      },
      "S1": {
        "ID": "S1",
        "Ports": {
          "ID": "Ports",
          "GigabitEthernet0/1": {
            "ID": "GigabitEthernet0/1",
            "Port Mode": {
              "ID": "Port Mode",
              "Name": "Port Mode",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            },
            "Native VLAN": {
              "ID": "Native VLAN",
              "Name": "Native VLAN",
              "Value": "99",
              "PointsPossible": 2,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          }
        }
      }
    }
  };

Я хочу обобщить данные в целом (totalPointsEarned, totalPointsPossible , totalItemsPossible, totalItemsComplete), а также каждым "ComparatorClass", хотя это должно храниться по имени, а не по номеру. Отображение номеров ComparatorClass в имена выглядит следующим образом:

    var comparatorClassIdToNameMap = {
        0:"Ip",
        1:"Routing",
        2:"Acl",
        3:"Nat",
        4:"Physical",
        5:"Switching",
        6:"Connectivity",
        7:"Logical",
        8:"All",
        9:"Encircling_Head",
        10:"Encircling_All"
    };

Таким образом, конечный результат должен выглядит примерно так:

{
  "totalPointsEarned": 25,
  "totalPointsPossible": 26,
  "totalItemsComplete": 24,
  "totalItemsPossible": 24,
  "Ip": {
    "pointsEarned": 7,
    "pointsPossible": 7,
    "itemsComplete": 6,
    "itemsPossible": 6,
    "ComparatorClassID": 0
  },
  "Routing": {
    "pointsEarned": 2,
    "pointsPossible": 2,
    "itemsComplete": 2,
    "itemsPossible": 2,
    "ComparatorClassID": 1
  },
  "Acl": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 2
  },
  "Nat": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 3
  },
  "Physical": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 4
  },
  "Switching": {
    "pointsEarned": 5,
    "pointsPossible": 6,
    "itemsComplete": 5,
    "itemsPossible": 5,
    "ComparatorClassID": 5
  },
  "Connectivity": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 6
  },
  "Logical": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 7
  },
  "All": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 8
  },
  "Encircling_Head": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 9
  },
  "Encircling_All": {
    "pointsEarned": 0,
    "pointsPossible": 0,
    "itemsComplete": 0,
    "itemsPossible": 0,
    "ComparatorClassID": 10
  }
}

pointsEarned / pointsPossible просто суммируются из необработанных данных, itemsComplete - это счетчик каждого листа, который имеет> 0 pointsEarned, а itemsPossible - это счетчик каждого листа, который имеет > 0 балловВозможно.

Любая помощь в как я мог бы использовать уменьшить, чтобы эффективно рассчитать эти значения (или какой-то другой метод) будет оценено. Я мог бы понять это с помощью серии for / ifs, но это кажется невероятно неэффективным.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 12 февраля 2020

Обновление

После того, как я написал приведенные ниже решения, я понял, что это действительно можно сделать проще, точно так, как вы предлагали, сопоставляя, а затем уменьшая результат. Это не точно картирование, это скорее извлечение листьев. Но тогда мы можем преобразовать ваш comparatorClassIdToNameMap в исходный объект вывода и просто уменьшить эти листья в нем.

Хотя приведенный ниже код не является красивым, он действительно решает проблему так, как вы запрашивали, что более чем я могу сказать для решений, которые я впервые написал.

// utility function
const findLeaves = (isLeaf) => (obj) =>
  isLeaf (obj) 
    ? [obj]
    : Object .values (obj) .flatMap (
        (v) => typeof v == 'object' ? findLeaves (isLeaf) (v) : []
      )

// main function
const transform = (actvity, nameMapping) => findLeaves (obj => 'PointsPossible' in obj) (activity) 
  .reduce((
    {totalPointsPossible, totalPointsEarned, ...rest}, 
    {PointsPossible, PointsEarned, ComparatorClass},
    _, __,  // index and array passed to `reduce` are ignored
    {PointsPossible: pp, PointsEarned: pe} = rest[nameMapping[ComparatorClass]]
  ) => ({                                                              // line 7
      totalPointsPossible: totalPointsPossible + PointsPossible,
      totalPointsEarned: totalPointsEarned + PointsEarned,
      ...rest,
      [nameMapping[ComparatorClass]]: {
        PointsPossible: PointsPossible + pp,
        PointsEarned: PointsEarned + pe,
        ComparatorClassId: ComparatorClass
      }
    }),                                                                 // line 16
    Object.entries(comparatorClassIdToNameMap).reduce((a, [k, v]) => ({ // line 17
      ... a,
      [v]: {
        PointsPossible: 0,
        PointsEarned: 0,
        ComparatorClassId: Number(k)
      }
    }), {totalPointsPossible: 0, totalPointsEarned: 0})                 // line 24
  )


// data
const activity = {Network: {ID: "Network", MLS: {ID: "MLS", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "GigabitEthernet0/2": {ID: "GigabitEthernet0/2", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "209.165.200.225", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Subnet Mask": {ID: "Subnet Mask", Name: "Subnet Mask", Value: "255.255.255.252", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: A: : 1": {ID: "2001: DB8: ACAD: A: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: A: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, SwitchPort: {ID: "SwitchPort", Name: "SwitchPort", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}}, Vlan10: {ID: "Vlan10", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.10.254", PointsPossible: 2, PointsEarned: 2, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 10: : 1": {ID: "2001: DB8: ACAD: 10: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 10: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan20: {ID: "Vlan20", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.20.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 20: : 1": {ID: "2001: DB8: ACAD: 20: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 20: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan30: {ID: "Vlan30", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.30.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 30: : 1": {ID: "2001: DB8: ACAD: 30: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 30: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan99: {ID: "Vlan99", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.99.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, Routes: {ID: "Routes", "Static Routes": {ID: "Static RoutesV2", Name: "Static Routes", Value: "", PointsPossible: 0, PointsEarned: 0, ComparatorClass: 1}, "IP Routing": {ID: "IP Routing", Name: "IP Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, Routesv6: {ID: "Routesv6", "IPv6 Unicast Routing": {ID: "Ipv6 Unicast Routing", Name: "IPv6 Unicast Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, VLANS: {ID: "VLANS", "VLAN 10": {ID: "10", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Staff", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 20": {ID: "20", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Student", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 30": {ID: "30", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Faculty", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}}}, S1: {ID: "S1", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 2, PointsEarned: 1, ComparatorClass: 5}}}}}};
const comparatorClassIdToNameMap = {0: "Ip", 1: "Routing", 2: "Acl", 3: "Nat", 4: "Physical", 5: "Switching", 6: "Connectivity", 7: "Logical", 8: "All", 9: "Encircling_Head", 10: "Encircling_All"}

// demo
console .log (
  transform (activity, comparatorClassIdToNameMap)
)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Строки 17 - 24 создают исходный объект со всеми свойствами, которые вы хотите использовать в выводе, используя comparatorClassIdToNameMap и добавляя свойства total*.

Строки 7 - 14 добавляют листовой узел в это. Это происходит внутри reduce, поэтому все конечные узлы добавляются, а полученный объект возвращается.

Приложение

В комментарии говорится, что вы не очень разбираетесь в JS , Вот альтернативная версия функции выше, написанная в стиле ES5. Этот может быть легче для чтения из других основных языков:

function transform (actvity, nameMapping) {
  var baseObj = {totalPointsPossible: 0, totalPointsEarned: 0}

  for (var id in nameMapping) {
    if (nameMapping .hasOwnProperty (id)) {
      var name = nameMapping [id]
      baseObj [name] = {
        PointsPossible: 0,
        PointsEarned: 0,
        ComparatorClassId: Number(id)
      }
    }
  }

  function isLeaf(obj) {
    return 'PointsPossible' in obj
  }

  var leaves = findLeaves (isLeaf) (activity)

  return leaves .reduce (
    function (acc, item) {
      acc .totalPointsPossible += item .PointsPossible
      acc .totalPointsEarned += item .PointsEarned
      var key = nameMapping [item .ComparatorClass]
      acc [key] .PointsPossible += item .PointsPossible
      acc [key] .PointsEarned += item .PointsEarned
      return acc
    },
    baseObj
  )
}

Этот делает то же самое, что и выше. Но он полон операторов присваивания, мутации и потока управления - всего, чего я лично стараюсь избегать. Приведенная выше версия позволяет избежать всего этого, используя деструктуризацию и другие более современные методы.


Оригинальное решение

Я больше не рекомендую ни одно из следующих двух предложений, но я думаю, что там в них все еще может быть значение.

Использование библиотеки

Я большой поклонник библиотеки функционального программирования Ramda (отказ от ответственности: я один его основных авторов), и я часто вижу такие проблемы глазами Рамды. Итак, во-первых, решение, которое довольно интенсивно использует Ramda:

// utility function
const findLeaves = (isLeaf) => (obj) =>
  isLeaf (obj) 
    ? [obj]
    : Object .values (obj) .flatMap (
        (v) => typeof v == 'object' ? findLeaves (isLeaf) (v) : []
      )

// helper functions
const summarize = pipe (
  findLeaves (has('PointsPossible')),
  groupBy (prop ('ComparatorClass')),
  map ( applySpec ({
    PointsPossible: pipe (pluck ('PointsPossible'), sum),
    PointsEarned: pipe (pluck ('PointsEarned'), sum),
    ComparatorClassID: pipe (head, prop ('ComparatorClass'))
  }))
)

  
const extractTotals = (activity, nameMapping, summary = summarize(activity)) =>
  reduce (
    (a, [id, name]) => 
      ({...a, [name]: summary [id] || 
      {PointsPossible: 0, PointsEarned: 0, ComparatorClassID: id}}), 
    {},
    toPairs (nameMapping)
  )

// main function  
const transform = (activity, nameMapping, totals = extractTotals (activity, nameMapping)) => 
  Object .assign (applySpec ({
    totalPointsPossible: pipe (pluck ('PointsPossible'), sum),
    totalPointsEarned: pipe (pluck ('PointsEarned'), sum),
  }) (values (totals)), totals)

// data
const activity = {Network: {ID: "Network", MLS: {ID: "MLS", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "GigabitEthernet0/2": {ID: "GigabitEthernet0/2", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "209.165.200.225", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Subnet Mask": {ID: "Subnet Mask", Name: "Subnet Mask", Value: "255.255.255.252", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: A: : 1": {ID: "2001: DB8: ACAD: A: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: A: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, SwitchPort: {ID: "SwitchPort", Name: "SwitchPort", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}}, Vlan10: {ID: "Vlan10", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.10.254", PointsPossible: 2, PointsEarned: 2, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 10: : 1": {ID: "2001: DB8: ACAD: 10: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 10: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan20: {ID: "Vlan20", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.20.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 20: : 1": {ID: "2001: DB8: ACAD: 20: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 20: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan30: {ID: "Vlan30", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.30.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 30: : 1": {ID: "2001: DB8: ACAD: 30: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 30: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan99: {ID: "Vlan99", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.99.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, Routes: {ID: "Routes", "Static Routes": {ID: "Static RoutesV2", Name: "Static Routes", Value: "", PointsPossible: 0, PointsEarned: 0, ComparatorClass: 1}, "IP Routing": {ID: "IP Routing", Name: "IP Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, Routesv6: {ID: "Routesv6", "IPv6 Unicast Routing": {ID: "Ipv6 Unicast Routing", Name: "IPv6 Unicast Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, VLANS: {ID: "VLANS", "VLAN 10": {ID: "10", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Staff", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 20": {ID: "20", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Student", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 30": {ID: "30", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Faculty", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}}}, S1: {ID: "S1", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 2, PointsEarned: 1, ComparatorClass: 5}}}}}};
const comparatorClassIdToNameMap = {0: "Ip", 1: "Routing", 2: "Acl", 3: "Nat", 4: "Physical", 5: "Switching", 6: "Connectivity", 7: "Logical", 8: "All", 9: "Encircling_Head", 10: "Encircling_All"}

// demo
console .log (
  transform (activity, comparatorClassIdToNameMap)
)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script>
const {pipe, has, groupBy, prop, map, applySpec, pluck, sum, head, reduce, toPairs, values} = R
</script>

Наша основная функция transform зависит от extractTotals, который зависит от summarize, который, в свою очередь, зависит от findLeaves. Очевидно, что поскольку extractTotals и summarize используются только для внутреннего использования, вы можете усвоить их внутри друг друга; Я бы не рекомендовал это. И findLeaves является рекурсивным, поэтому не может быть обработан таким образом.

findLeaves принимает функцию предиката, чтобы проверить, является ли узел листом и объектом, и рекурсивно находит все узлы, о которых сообщается, что листья. Он не обрабатывает массивы, но это не было частью ваших требований; расширение не составит труда.

Если мы вызовем findLeaves(obj => 'PointsPossible' in obj)(activity), мы получим плоский массив, содержащий все конечные узлы, например:

[
    {
        "ID": "Port Mode",
        "Name": "Port Mode",
        "Value": "0",
        "PointsPossible": 1,
        "PointsEarned": 1,
        "ComparatorClass": 8
    },
    {
        "ID": "Native VLAN",
        "Name": "Native VLAN",
        "Value": "99",
        "PointsPossible": 1,
        "PointsEarned": 1,
        "ComparatorClass": 5
    },
    // ...
]

summarize использует findLeaves и ряд функций Ramda, превращающих ваш activity в

{
    "0": {
        "PointsPossible": 15,
        "PointsEarned": 15,
        "ComparatorClassID": 0
    },
    "1": {
        "PointsPossible": 2,
        "PointsEarned": 2,
        "ComparatorClassID": 1
    },
    // ...
}

Мы получаем почти весь путь с extractTotals, который сочетает в себе вышеупомянутое с вашим comparatorClassIdToNameMap, чтобы получить это:

{
    "Ip": {
        "PointsPossible": 15,
        "PointsEarned": 15,
        "ComparatorClassID": 0
    },
    "Routing": {
        "PointsPossible": 2,
        "PointsEarned": 2,
        "ComparatorClassID": 1
    },
    // ...
}

Единственное, чего не хватает, это общих итогов. Это остается для transform, который берет результат вышеупомянутого и добавляет эти итоговые значения к результирующему объекту:

{
    "totalPointsPossible": 26,
    "totalPointsEarned": 25,
    "Ip": {
        "PointsPossible": 15,
        "PointsEarned": 15,
        "ComparatorClassID": 0
    },
    "Routing": {
        "PointsPossible": 2,
        "PointsEarned": 2,
        "ComparatorClassID": 1
    },
    // ...
}

( Примечание , что здесь нет ничего, чтобы обрабатывать ваши itemsComplete или itemsPossible. Я не вижу в данных ничего, что могло бы помочь с этим, но я предполагаю, что это были бы простые дополнения к описанному выше.)

Эта проблема является моей обычной техникой выполнения. небольшие преобразования по одному, всегда пытаясь приблизиться к требуемому решению. Там могут быть полезные способы объединить шаги. Я оставляю это вам.

Без библиотеки

В такой библиотеке, как Рамда, нет магов c. Мы можем написать повторно используемые версии его функций самостоятельно. И это часто улучшает многие части нашего кода.

Итак, мы могли бы сделать то же самое самостоятельно, написав собственную версию этих функций Ramda. Большинство тривиально. Только applySpec, groupBy и mapObj имеют любую сложность. Обратите внимание, что в вышеприведенной версии Ramda мы использовали map вместо Рамады mapObj. Хотя мы могли бы написать map для обработки массивов или объектов, здесь проще сделать две функции.

// utility functions
const pipe = (...fns) => (arg) => 
  fns.reduce((a, fn) => fn(a), arg) 
const map = (fn) => (xs) => 
  xs .map (x => fn (x))
const mapObj = (fn) => (obj) => 
  Object .fromEntries (map (([k, v]) => [k, fn (v)]) (Object .entries (obj)))
const has = (name) => (obj) => 
  name in obj
const prop = (name) => (obj) =>
  obj [name]
const pluck = (name) => 
  map (prop (name))
const head = (xs) => 
  xs [0]
const sum = (ns) =>
  ns .reduce ((a, b) => a + b, 0)
const applySpec = (spec) => (obj) =>
  Object .entries (spec) .reduce ((a, [k, fn]) => ({...a, [k]: fn (obj)}), {})
const groupBy = (fn) => (xs) =>
  xs .reduce ((a, x, _, __, k = fn(x)) => ({...a, [k]: [...(a[k] || []), x]}), {})

const findLeaves = (isLeaf) => (obj) =>
  isLeaf (obj) 
    ? [obj]
    : Object .values (obj) .flatMap (
        (v) => typeof v == 'object' ? findLeaves (isLeaf) (v) : []
      )

// helper functions
const summarize = pipe (
  findLeaves (has ('PointsPossible')),
  groupBy (prop ('ComparatorClass')),
  mapObj ( applySpec ({
    PointsPossible: pipe (pluck ('PointsPossible'), sum),
    PointsEarned: pipe (pluck ('PointsEarned'), sum),
    ComparatorClassID: pipe (head, prop ('ComparatorClass'))
  }))
)

const extractTotals = (activity, nameMapping, summary = summarize(activity)) =>
  Object .entries (nameMapping)
    .reduce (
      (a, [id, name]) => ({...a, [name]: summary[id] || {PointsPossible: 0, PointsEarned: 0, ComparatorClassID: id}}), 
      {}
    )

// main function
const transform = (activity, nameMapping) => {
  const totals = extractTotals (activity, nameMapping)
  return Object .assign (applySpec ({
    totalPointsPossible: pipe (pluck ('PointsPossible'), sum),
    totalPointsEarned: pipe (pluck ('PointsEarned'), sum),
  }) (Object .values (totals)), totals)
}

// data
const activity = {Network: {ID: "Network", MLS: {ID: "MLS", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "GigabitEthernet0/2": {ID: "GigabitEthernet0/2", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "209.165.200.225", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Subnet Mask": {ID: "Subnet Mask", Name: "Subnet Mask", Value: "255.255.255.252", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: A: : 1": {ID: "2001: DB8: ACAD: A: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: A: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, SwitchPort: {ID: "SwitchPort", Name: "SwitchPort", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}}, Vlan10: {ID: "Vlan10", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.10.254", PointsPossible: 2, PointsEarned: 2, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 10: : 1": {ID: "2001: DB8: ACAD: 10: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 10: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan20: {ID: "Vlan20", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.20.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 20: : 1": {ID: "2001: DB8: ACAD: 20: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 20: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan30: {ID: "Vlan30", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.30.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "(deprecated) IPv6 Addresses": {ID: "Ipv6 Address", "2001: DB8: ACAD: 30: : 1": {ID: "2001: DB8: ACAD: 30: : 1", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "2001: DB8: ACAD: 30: : 1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}, "Prefix Length": {ID: "Prefix", Name: "Prefix Length", Value: "64", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}}, Vlan99: {ID: "Vlan99", "IP Address": {ID: "IP Address", Name: "IP Address", Value: "192.168.99.254", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 0}}}, Routes: {ID: "Routes", "Static Routes": {ID: "Static RoutesV2", Name: "Static Routes", Value: "", PointsPossible: 0, PointsEarned: 0, ComparatorClass: 1}, "IP Routing": {ID: "IP Routing", Name: "IP Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, Routesv6: {ID: "Routesv6", "IPv6 Unicast Routing": {ID: "Ipv6 Unicast Routing", Name: "IPv6 Unicast Routing", Value: "1", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 1}}, VLANS: {ID: "VLANS", "VLAN 10": {ID: "10", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Staff", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 20": {ID: "20", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Student", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}, "VLAN 30": {ID: "30", "VLAN Name": {ID: "VLAN Name", Name: "VLAN Name", Value: "Faculty", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 5}}}}, S1: {ID: "S1", Ports: {ID: "Ports", "GigabitEthernet0/1": {ID: "GigabitEthernet0/1", "Port Mode": {ID: "Port Mode", Name: "Port Mode", Value: "0", PointsPossible: 1, PointsEarned: 1, ComparatorClass: 8}, "Native VLAN": {ID: "Native VLAN", Name: "Native VLAN", Value: "99", PointsPossible: 2, PointsEarned: 1, ComparatorClass: 5}}}}}};
const comparatorClassIdToNameMap = {0: "Ip", 1: "Routing", 2: "Acl", 3: "Nat", 4: "Physical", 5: "Switching", 6: "Connectivity", 7: "Logical", 8: "All", 9: "Encircling_Head", 10: "Encircling_All"}

// demo
console .log (
  transform (activity, comparatorClassIdToNameMap)
)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Здесь используется та же разбивка проблемы, что и выше, просто пишется пользовательская версия функций Ramda. Если вы хотите узнать больше о каком-либо из них (кроме mapObj, который в Ramda указан как map), см. Документацию Ramda . Эти реализации проще, чем у Ramda, и, как правило, менее гибкие, но они работают для наших случаев.

Примечание

Мне показалось, что это интересная проблема, которую стоит попробовать. Но я почти сразу сдался из-за стены данных. Очень трудно пролистать столько данных, чтобы попытаться определить требования. В следующий раз просмотрите Как создать минимальный воспроизводимый пример перед публикацией. Вы должны быть в состоянии сделать намного меньший контрольный пример, который все еще демонстрирует цель.

0 голосов
/ 10 февраля 2020

Это ни в коем случае не красиво (5 вложенных циклов forEach) и требует очистки. но основная предпосылка c есть и должна доставить вас туда, куда вам нужно go.

const comparatorClassIdToNameMap = {
      0:"Ip",
      1:"Routing",
      2:"Acl",
      3:"Nat",
      4:"Physical",
      5:"Switching",
      6:"Connectivity",
      7:"Logical",
      8:"All",
      9:"Encircling_Head",
      10:"Encircling_All"
  }, activity = {
    "Network": {
      "ID": "Network",
      "MLS": {
        "ID": "MLS",
        "Ports": {
          "ID": "Ports",
          "GigabitEthernet0/1": {
            "ID": "GigabitEthernet0/1",
            "Port Mode": {
              "ID": "Port Mode",
              "Name": "Port Mode",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            },
            "Native VLAN": {
              "ID": "Native VLAN",
              "Name": "Native VLAN",
              "Value": "99",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "GigabitEthernet0/2": {
            "ID": "GigabitEthernet0/2",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "209.165.200.225",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "Subnet Mask": {
              "ID": "Subnet Mask",
              "Name": "Subnet Mask",
              "Value": "255.255.255.252",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:A::1": {
                "ID": "2001:DB8:ACAD:A::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:A::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            },
            "SwitchPort": {
              "ID": "SwitchPort",
              "Name": "SwitchPort",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            }
          },
          "Vlan10": {
            "ID": "Vlan10",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.10.254",
              "PointsPossible": 2,
              "PointsEarned": 2,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:10::1": {
                "ID": "2001:DB8:ACAD:10::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:10::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan20": {
            "ID": "Vlan20",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.20.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:20::1": {
                "ID": "2001:DB8:ACAD:20::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:20::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan30": {
            "ID": "Vlan30",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.30.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            },
            "(deprecated) IPv6 Addresses": {
              "ID": "Ipv6 Address",
              "2001:DB8:ACAD:30::1": {
                "ID": "2001:DB8:ACAD:30::1",
                "IP Address": {
                  "ID": "IP Address",
                  "Name": "IP Address",
                  "Value": "2001:DB8:ACAD:30::1",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                },
                "Prefix Length": {
                  "ID": "Prefix",
                  "Name": "Prefix Length",
                  "Value": "64",
                  "PointsPossible": 1,
                  "PointsEarned": 1,
                  "ComparatorClass": 0
                }
              }
            }
          },
          "Vlan99": {
            "ID": "Vlan99",
            "IP Address": {
              "ID": "IP Address",
              "Name": "IP Address",
              "Value": "192.168.99.254",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 0
            }
          }
        },
        "Routes": {
          "ID": "Routes",
          "Static Routes": {
            "ID": "Static RoutesV2",
            "Name": "Static Routes",
            "Value": "",
            "PointsPossible": 0,
            "PointsEarned": 0,
            "ComparatorClass": 1
          },
          "IP Routing": {
            "ID": "IP Routing",
            "Name": "IP Routing",
            "Value": "1",
            "PointsPossible": 1,
            "PointsEarned": 1,
            "ComparatorClass": 1
          }
        },
        "Routesv6": {
          "ID": "Routesv6",
          "IPv6 Unicast Routing": {
            "ID": "Ipv6 Unicast Routing",
            "Name": "IPv6 Unicast Routing",
            "Value": "1",
            "PointsPossible": 1,
            "PointsEarned": 1,
            "ComparatorClass": 1
          }
        },
        "VLANS": {
          "ID": "VLANS",
          "VLAN 10": {
            "ID": "10",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Staff",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "VLAN 20": {
            "ID": "20",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Student",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          },
          "VLAN 30": {
            "ID": "30",
            "VLAN Name": {
              "ID": "VLAN Name",
              "Name": "VLAN Name",
              "Value": "Faculty",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          }
        }
      },
      "S1": {
        "ID": "S1",
        "Ports": {
          "ID": "Ports",
          "GigabitEthernet0/1": {
            "ID": "GigabitEthernet0/1",
            "Port Mode": {
              "ID": "Port Mode",
              "Name": "Port Mode",
              "Value": "0",
              "PointsPossible": 1,
              "PointsEarned": 1,
              "ComparatorClass": 8
            },
            "Native VLAN": {
              "ID": "Native VLAN",
              "Name": "Native VLAN",
              "Value": "99",
              "PointsPossible": 2,
              "PointsEarned": 1,
              "ComparatorClass": 5
            }
          }
        }
      }
    }
  };
toActivityObjectsArr(activity);

function toActivityObjectsArr (obj) {
  const arr = [], searchKey = 'ComparatorClass';
  Object.keys(obj.Network).forEach(function (networkChild) {
    Object.keys(obj.Network[networkChild]).forEach(function (networkGrandChild) {
      networkGrandChild == searchKey && arr.push(obj.Network[networkChild]);
      Object.keys(obj.Network[networkChild][networkGrandChild]).forEach(function (networkGreatGrandChild) {
        networkGreatGrandChild == searchKey && arr.push(obj.Network[networkChild][networkGrandChild]);
        Object.keys(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild]).forEach(function (networkGreatGreatGrandChild) {
          networkGreatGreatGrandChild == searchKey && arr.push(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild]);
          Object.keys(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild]).forEach(function (networkGreatGreatGreatGrandChild) {
            networkGreatGreatGreatGrandChild == searchKey && arr.push(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild]);
            Object.keys(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild][networkGreatGreatGreatGrandChild]).forEach(function (networkGreatGreatGreatGreatGrandChild) {
              networkGreatGreatGreatGreatGrandChild == searchKey && arr.push(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild][networkGreatGreatGreatGrandChild]);
              Object.keys(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild][networkGreatGreatGreatGrandChild][networkGreatGreatGreatGreatGrandChild]).forEach(function (networkGreatGreatGreatGreatGreatGrandChild) {
                networkGreatGreatGreatGreatGreatGrandChild == searchKey && arr.push(obj.Network[networkChild][networkGrandChild][networkGreatGrandChild][networkGreatGreatGrandChild][networkGreatGreatGreatGrandChild][networkGreatGreatGreatGreatGrandChild]);
              });
            });
          });
        });
      });
    });
  });
  createAnswerObj(arr);
}
function createAnswerObj(arr) {
  const answerObj = {};
  answerObj.totalPossiblePoints = getTotalPossiblePoints(arr);
  answerObj.totalPointsEarned = getTotalPointsEarned(arr);
  Object.keys(comparatorClassIdToNameMap).forEach(function (k) {
    answerObj[comparatorClassIdToNameMap[k]] = {
      pointsEarned: getPointsEarnedByComparator(arr, k),
      pointsPossible: getPointsPossibleByComparator(arr, k),
      ComparatorClassID: k
    };
  });
  console.log(answerObj);
}
function getTotalPossiblePoints(arr) {
  return arr.reduce(function (total, obj) {
    return total += obj.PointsPossible;
  }, 0);
}
function getPointsPossibleByComparator(arr, k) {
  return arr.reduce(function (total, obj) {
    return obj.ComparatorClass == k ? (total += obj.PointsPossible) : total;
  }, 0);
}
function getTotalPointsEarned(arr) {
  return arr.reduce(function (total, obj) {
    return total += obj.PointsEarned;
  }, 0);
}
function getPointsEarnedByComparator(arr, k) {
  return arr.reduce(function (total, obj) {
    return obj.ComparatorClass == k ? (total += obj.PointsEarned) : total;
  }, 0);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...