Search code examples
javascriptalgorithmrecursionbinary-search-tree

How to get and set the sum of the values of an array object with infinite depth of this array?


I need to convert an array of objects into a new array with objects including a new type field percent. The task is this - each object in the array has a defaultWeight field with a numeric value. In this array, I have to add the defaultWeight of the current array to the sum of sumArray, then I take the defaultWeight value, divide by sumArray and multiply by 100 (defaultWeight / sumArray * 100). Thus, I get the percentage of the number that I have to put in the new percent field, which depth of these arrays would not be encountered in the future. Help me, I've been racking my head for the second day. Below is an example:

[
    {
        "id": 1,
        "name": "Блок «Тест1»",
        "weight": null,
        "defaultWeight": 0.3,
        "children": [
            {
                "id": 1,
                "name": "Планирование активностей",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 2,
                "name": "Выполнение плана по визитам",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 3,
                "name": "Выполнение плана по групповым мероприятиям",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 4,
                "name": "Выполнение маркетинговой стратегии (включает три KPI)",
                "weight": null,
                "defaultWeight": 1,
                "children": [
                    {
                        "id": 5,
                        "name": "Охват базы",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    },
                    {
                        "id": 6,
                        "name": "Соблюдение кратности посещения специалистов",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    },
                    {
                        "id": 7,
                        "name": "Интервальность",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    }
                ],
                "children": [
                    {
                        "id": 5,
                        "name": "Охват базы",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    },
                    {
                        "id": 6,
                        "name": "Соблюдение кратности посещения специалистов",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    },
                    {
                        "id": 7,
                        "name": "Интервальность",
                        "weight": null,
                        "defaultWeight": 1,
                        "children": []
                    }
                ]
            },
            {
                "id": 14,
                "name": "Время, проведённое на визитах в день",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 15,
                "name": "Средняя длительность визита",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 16,
                "name": "Доля рабочего дня на активности",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 17,
                "name": "Геолокация",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            }
        ]
    },
    {
        "id": 2,
        "name": "Блок «Тест2»",
        "weight": null,
        "defaultWeight": 0.2,
        "children": [
            {
                "id": 8,
                "name": "% выполнения «Двойной визит» (оценка РМа)",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 9,
                "name": "% выполнения «Аудит» (оценка РМа)",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 10,
                "name": "% выполнения «Двойной визит» (оценка Аудитора)",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 11,
                "name": "% выполнения «Аудит» (оценка Аудитора)",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            },
            {
                "id": 19,
                "name": "Средневзвешенное с весами оценки РМа и Аудитора",
                "weight": null,
                "defaultWeight": 1,
                "children": []
            }
        ]
    },
    {
        "id": 3,
        "name": "Блок «Тест3»",
        "weight": null,
        "defaultWeight": 0.5,
        "children": [
            {
                "id": 26,
                "name": "Выполнение плана продаж",
                "weight": null,
                "defaultWeight": 0.9,
                "children": []
            },
            {
                "id": 27,
                "name": "Темп прироста",
                "weight": null,
                "defaultWeight": 0.025,
                "children": []
            },
            {
                "id": 28,
                "name": "Evolution Index",
                "weight": null,
                "defaultWeight": 0.025,
                "children": []
            },
            {
                "id": 29,
                "name": "Динамика фактической доли на рынке",
                "weight": null,
                "defaultWeight": 0.025,
                "children": []
            },
            {
                "id": 30,
                "name": "Сравнение фактической доли на рынке с долей по РФ",
                "weight": null,
                "defaultWeight": 0.025,
                "children": []
            }
        ]
    }
]

Should turn into:

[
    {
        "id": 1,
        "name": "Блок «SoReX»",
        "weight": null,
        "defaultWeight": 0.3,
        "children": [
            {
                "id": 1,
                "name": "Планирование активностей",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 2,
                "name": "Выполнение плана по визитам",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 3,
                "name": "Выполнение плана по групповым мероприятиям",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 4,
                "name": "Выполнение маркетинговой стратегии (включает три KPI)",
                "weight": null,
                "defaultWeight": 1,
                "children": [
                    {
                        "id": 5,
                        "name": "Охват базы",
                        "weight": null,
                        "defaultWeight": 1,
                        "percent": 33.33,
                        "children": []
                    },
                    {
                        "id": 6,
                        "name": "Соблюдение кратности посещения специалистов",
                        "weight": null,
                        "defaultWeight": 1,
                        "percent": 33.33,
                        "children": []
                    },
                    {
                        "id": 7,
                        "name": "Интервальность",
                        "weight": null,
                        "defaultWeight": 1,
                        "percent": 33.33,
                        "children": []
                    }
                ]
            {
                "id": 14,
                "name": "Время, проведённое на визитах в день",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 15,
                "name": "Средняя длительность визита",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 16,
                "name": "Доля рабочего дня на активности",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            },
            {
                "id": 17,
                "name": "Геолокация",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 12.5
            }
        ],
        "percent": 30
    },
    {
        "id": 2,
        "name": "Блок «Качество»",
        "weight": null,
        "defaultWeight": 0.2,
        "children": [
            {
                "id": 8,
                "name": "% выполнения «Двойной визит» (оценка РМа)",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 20
            },
            {
                "id": 9,
                "name": "% выполнения «Аудит» (оценка РМа)",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 20
            },
            {
                "id": 10,
                "name": "% выполнения «Двойной визит» (оценка Аудитора)",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 20
            },
            {
                "id": 11,
                "name": "% выполнения «Аудит» (оценка Аудитора)",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 20
            },
            {
                "id": 19,
                "name": "Средневзвешенное с весами оценки РМа и Аудитора",
                "weight": null,
                "defaultWeight": 1,
                "children": [],
                "percent": 20
            }
        ],
        "percent": 20
    },
    {
        "id": 3,
        "name": "Блок «Рынок»",
        "weight": null,
        "defaultWeight": 0.5,
        "children": [
            {
                "id": 26,
                "name": "Выполнение плана продаж",
                "weight": null,
                "defaultWeight": 0.9,
                "children": [],
                "percent": 90
            },
            {
                "id": 27,
                "name": "Темп прироста",
                "weight": null,
                "defaultWeight": 0.025,
                "children": [],
                "percent": 2.5
            },
            {
                "id": 28,
                "name": "Evolution Index",
                "weight": null,
                "defaultWeight": 0.025,
                "children": [],
                "percent": 2.5
            },
            {
                "id": 29,
                "name": "Динамика фактической доли на рынке",
                "weight": null,
                "defaultWeight": 0.025,
                "children": [],
                "percent": 2.5
            },
            {
                "id": 30,
                "name": "Сравнение фактической доли на рынке с долей по РФ",
                "weight": null,
                "defaultWeight": 0.025,
                "children": [],
                "percent": 2.5
            }
        ],
        "percent": 50
    }
]

I tried to solve this problem by recursion, but my knowledge of algorithms, unfortunately, leaves much to be desired

public setSum(weight: any, id?: number) {

const calculation = (weights) => weights.map(block => {
  return {
    ...block,
    percent: block.defaultWeight / this.sumBlock * 100
  };
});

if (weight.length !== []) {
  this.sumBlock = weight.reduce((acc, block) => {
    return acc += block.defaultWeight;
  }, 0);

  if (id) {
    this.cloneBlocks = this.cloneBlocks.map(item => {
      if (item.id === id) {
        return {
          ...item,
          groups: weight.map(block => {
            return {
              ...block,
              percent: block.defaultWeight / this.sumBlock * 100
            };
          })
        };
      } else {
        return { ...item };
      }
    });
    return;
  } else {
    this.cloneBlocks = weight.map(block => {
      return {
        ...block,
        percent: block.defaultWeight / this.sumBlock * 100
      };
    });
  }
}

weight.forEach(block => {
  if (block.groups) {
    this.setSum(block.groups, block.id);
  }
});

console.log(this.cloneBlocks);

}


Solution

  • I think something like this should do the job, see comments for details on how it works:

    // Start of Dataset
    const object = [
        {
            "id": 1,
            "name": "Блок «Тест1»",
            "weight": null,
            "defaultWeight": 0.3,
            "children": [
                {
                    "id": 1,
                    "name": "Планирование активностей",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 2,
                    "name": "Выполнение плана по визитам",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 3,
                    "name": "Выполнение плана по групповым мероприятиям",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 4,
                    "name": "Выполнение маркетинговой стратегии (включает три KPI)",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": [
                        {
                            "id": 5,
                            "name": "Охват базы",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        },
                        {
                            "id": 6,
                            "name": "Соблюдение кратности посещения специалистов",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        },
                        {
                            "id": 7,
                            "name": "Интервальность",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        }
                    ],
                    "children": [
                        {
                            "id": 5,
                            "name": "Охват базы",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        },
                        {
                            "id": 6,
                            "name": "Соблюдение кратности посещения специалистов",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        },
                        {
                            "id": 7,
                            "name": "Интервальность",
                            "weight": null,
                            "defaultWeight": 1,
                            "children": []
                        }
                    ]
                },
                {
                    "id": 14,
                    "name": "Время, проведённое на визитах в день",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 15,
                    "name": "Средняя длительность визита",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 16,
                    "name": "Доля рабочего дня на активности",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 17,
                    "name": "Геолокация",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                }
            ]
        },
        {
            "id": 2,
            "name": "Блок «Тест2»",
            "weight": null,
            "defaultWeight": 0.2,
            "children": [
                {
                    "id": 8,
                    "name": "% выполнения «Двойной визит» (оценка РМа)",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 9,
                    "name": "% выполнения «Аудит» (оценка РМа)",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 10,
                    "name": "% выполнения «Двойной визит» (оценка Аудитора)",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 11,
                    "name": "% выполнения «Аудит» (оценка Аудитора)",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                },
                {
                    "id": 19,
                    "name": "Средневзвешенное с весами оценки РМа и Аудитора",
                    "weight": null,
                    "defaultWeight": 1,
                    "children": []
                }
            ]
        },
        {
            "id": 3,
            "name": "Блок «Тест3»",
            "weight": null,
            "defaultWeight": 0.5,
            "children": [
                {
                    "id": 26,
                    "name": "Выполнение плана продаж",
                    "weight": null,
                    "defaultWeight": 0.9,
                    "children": []
                },
                {
                    "id": 27,
                    "name": "Темп прироста",
                    "weight": null,
                    "defaultWeight": 0.025,
                    "children": []
                },
                {
                    "id": 28,
                    "name": "Evolution Index",
                    "weight": null,
                    "defaultWeight": 0.025,
                    "children": []
                },
                {
                    "id": 29,
                    "name": "Динамика фактической доли на рынке",
                    "weight": null,
                    "defaultWeight": 0.025,
                    "children": []
                },
                {
                    "id": 30,
                    "name": "Сравнение фактической доли на рынке с долей по РФ",
                    "weight": null,
                    "defaultWeight": 0.025,
                    "children": []
                }
            ]
        }
    ];
    
    // Note: This function will mutate its parameter
    function setPercent(dataset) {
      // Sum the total defaultWeight for siblings
      const totalWeight = dataset.reduce((acc, next) => acc + next.defaultWeight, 0);
      dataset.forEach(node => {
        // Set percent of siblings if totalWeight is different from 0
        if (totalWeight !== 0) {
          node.percent = node.defaultWeight / totalWeight * 100;
        }
        // Recurse for children
        setPercent(node.children);
      });
    }
    
    // Run function, but clone object to avoid mutation
    const dataset = structuredClone(object);
    setPercent(dataset);
    
    console.log(dataset);