JamesE
JamesE

Reputation: 3923

d3 hierarchy ability to average children node values

I have a d3 visualization that has a JSON object similar to the following where I would like to average up the value of score on the lowest nodes and dynamically add that average to the parent node above...and so on. It doesn't look like d3 has an easy method to do this. What I'd like is to have the final JSON output look like the second example.

 {
  "name": "A1",
  "children": [
    {
      "name": "B1",
      "children": [
        {
          "name": "B1-C1",
          "children": [
            {
              "name": "B1-C1-D1",
              "children": [
                {
                  "name": "B1-C1-D1-E1",
                  "value": 30,
                  "score": 0.8
                },
                {
                  "name": "B1-C1-D1-E2",
                  "value": 35,
                  "score": 0.5
                }
              ]
            },
            {
              "name": "B1-C1-D2",
              "children": [
                {
                  "name": "B1-C1-D2-E1",
                  "value": 31,
                  "score": 0.4
                },
                {
                  "name": "B1-C1-D2-E2",
                  "value": 23,
                  "score": 0.7
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

What I'd like the final JSON object to look like:

{
  "name": "A1",
  "scoreAvg": 0.625,
  "children": [
    {
      "name": "B1",
      "scoreAvg": 0.625,
      "children": [
        {
          "name": "B1-C1",
          "scoreAvg": 0.625,
          "children": [
            {
              "name": "B1-C1-D1",
              "scoreAvg": 0.7,
              "children": [
                {
                  "name": "B1-C1-D1-E1",
                  "value": 30,
                  "score": 0.8
                },
                {
                  "name": "B1-C1-D1-E2",
                  "value": 35,
                  "score": 0.6
                }
              ]
            },
            {
              "name": "B1-C1-D2",
              "scoreAvg": 0.55,
              "children": [
                {
                  "name": "B1-C1-D2-E1",
                  "value": 31,
                  "score": 0.4
                },
                {
                  "name": "B1-C1-D2-E2",
                  "value": 23,
                  "score": 0.7
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Upvotes: 2

Views: 247

Answers (2)

Marcos Luis Delgado
Marcos Luis Delgado

Reputation: 1429

You can use a recursive function:

const obj = {
  "name": "A1",
  "children": [{
    "name": "B1",
    "children": [{
      "name": "B1-C1",
      "children": [{
          "name": "B1-C1-D1",
          "children": [{
              "name": "B1-C1-D1-E1",
              "value": 30,
              "score": 0.8
            },
            {
              "name": "B1-C1-D1-E2",
              "value": 35,
              "score": 0.5
            }
          ]
        },
        {
          "name": "B1-C1-D2",
          "children": [{
              "name": "B1-C1-D2-E1",
              "value": 31,
              "score": 0.4
            },
            {
              "name": "B1-C1-D2-E2",
              "value": 23,
              "score": 0.7
            }
          ]
        }
      ]
    }]
  }]
}

function getWithAverageScore(objToRecurse) {
  // If I have a score I am already done
	if (objToRecurse.score) {
		return objToRecurse;
	}
  // Otherwise, I get my children with their average score
	const children = objToRecurse.children.map(getWithAverageScore);

	return {
		...objToRecurse,
		children,
    // And I set my scoreAvg to their average (score or scoreAvg)
		scoreAvg: children.reduce((total, { score, scoreAvg }) => total + (score || scoreAvg), 0) / children.length
	};
}

console.log(JSON.stringify(getWithAverageScore(obj), null, 2))

Upvotes: 1

sriam980980
sriam980980

Reputation: 2028

let o = {
  "name": "A1",
  "children": [
    {
      "name": "B1",
      "children": [
        {
          "name": "B1-C1",
          "children": [
            {
              "name": "B1-C1-D1",
              "children": [
                {
                  "name": "B1-C1-D1-E1",
                  "value": 30,
                  "score": 0.8
                },
                {
                  "name": "B1-C1-D1-E2",
                  "value": 35,
                  "score": 0.5
                }
              ]
            },
            {
              "name": "B1-C1-D2",
              "children": [
                {
                  "name": "B1-C1-D2-E1",
                  "value": 31,
                  "score": 0.4
                },
                {
                  "name": "B1-C1-D2-E2",
                  "value": 23,
                  "score": 0.7
                }
              ]
            }
          ]
        }
      ]
    }
  ]
};


function avgUp(object){
  object.avgScore = 0;
if(object.children){
  for(child of object.children){
    object.avgScore += avgUp(child);
   }
object.avgScore =  object.avgScore /Math.max(1,object.children.length);
   return object.avgScore;
}else{
return object.score;
}
}

avgUp(o);

console.log(JSON.stringify(o));

Upvotes: 1

Related Questions