Home > Net >  Reducing an array to return grouped data from schools to local authorities
Reducing an array to return grouped data from schools to local authorities

Time:03-22

I have a bit of JS code that I use to draw svg circles on the page. The circles are a based on school data and the size of the circle is dependant on the SchoolCapacity field. The problem is that my array schooldata contains 100s of elements and the circles are very crowded and overlap over each other. My question is how can I rewrite the process schools data code to display Local Authorities (LA (Name)) and their school capacities as draw those as circles instead.

Array of school data:

const schooldata = [{
    "URN": 135691,
    "LA (code)": 938,
    "LA (name)": "West Sussex",
    "EstablishmentNumber": 6228,
    "EstablishmentName": "Seadown School",
    "SchoolCapacity": 30,
    "Postcode": "BN11 2BE"
  }, {
    "URN": 112080,
    "LA (code)": 908,
    "LA (name)": "Cornwall",
    "EstablishmentNumber": 6084,
    "EstablishmentName": "St Ia School",
    "SchoolCapacity": 45,
    "Postcode": "TR26 2SF"
  },
    {
    "URN": 130842,
    "LA (code)": 938,
    "LA (name)": "West Sussex",
    "EstablishmentNumber": 8003,
    "EstablishmentName": "Greater Brighton Metropolitan College",
    "SchoolCapacity": "",
    "Postcode": "BN12 6NU"
  },

JS Business logic

  function getcoordinates(postcode) {
    let url = "https://api.getthedata.com/postcode/"   postcode.replace(" ", " ");
    try {
      return fetch(url).then(function(response) {
        return response.json(); // Process it inside the `then`
      });
    } catch (error) {
      console.log(error);
    }
  }
  //svg setup
  var svgns = "http://www.w3.org/2000/svg",
    container = document.getElementById('cont');
  
  function drawcircle(easting, northing, size, label, identifier) {
    var xScale = (container.width.baseVal.value - 20) / (700000);
    var x = 10   (xScale * easting);
    var yScale = (container.height.baseVal.value - 20) / (700000);
    var y = 10   (yScale * (700000 - northing));
    var sizeScale = 100;
    var radius = size / sizeScale;
  
    //draw or update the svg circle
    var circle = document.getElementById("Circle_"   identifier);
    if (circle == null) {
      circle = document.createElementNS(svgns, 'circle');
    }
    circle.setAttributeNS(null, 'id', "Circle_"   identifier);
    circle.setAttributeNS(null, 'cx', x);
    circle.setAttributeNS(null, 'cy', y);
    circle.setAttributeNS(null, 'r', radius);
    circle.setAttributeNS(null, 'style', 'fill: #c6605e; stroke: none; stroke-width: 1px;');
    container.appendChild(circle);
  
    //draw or update the circle label
    var newText = document.getElementById("Circle_Label_"   identifier);
    if (newText == null) {
      newText = document.createElementNS(svgns, "text");
      var textNode = document.createTextNode(label);
      newText.appendChild(textNode);
    }
    newText.setAttributeNS(null, 'id', "Circle_Label_"   identifier);
    newText.setAttributeNS(null, "x", x);
    newText.setAttributeNS(null, "y", y);
    newText.setAttributeNS(null, "font-size", "10");
    var textNode = document.createTextNode(label);
    newText.replaceChild(textNode, newText.childNodes[0]);
    container.appendChild(newText);
  }
  
  **//process schools data** -- TODO: change to LA instead of schools
  let schools = schooldata.reduce(function(allSchools, school) {
    allSchools[school.EstablishmentName] = {
      SchoolCapacity: school.SchoolCapacity || 0,
      coordinates: []
    };
    getcoordinates(school.Postcode).then(function(data) {
      allSchools[school.EstablishmentName].coordinates.push([data.data.easting, data.data.northing]);
      drawcircle(data.data.easting, data.data.northing, school.SchoolCapacity, school.EstablishmentName, school.URN);
    });
    return allSchools
  }, {});

HTML

<!DOCTYPE html>
<meta charset="utf-8">
          
<!-- Create an element where the map will take place -->
<svg id="cont" width="900" height="900"></svg>

I assume I need to do some Array reducing?

CodePudding user response:

First, reduce your schools to Local Authorities using Array.reduce():

const locAuthorities = schooldata.reduce(function (prev, curr) {
  const laCode = curr["LA (code)"]
  const laName = curr["LA (name)"]
  if (prev[laCode]) {
    prev[laCode].SchoolCapacity  = curr.SchoolCapacity || 0
  } else {
    prev[laCode] = {
      "LA (code)": laCode,
      "LA (name)": laName,
      "SchoolCapacity": curr.SchoolCapacity,
      "Postcode": curr.Postcode,
      "URN": curr.URN
    }
  }
  return prev
}, {})

const locAuthoritiesArray = Object.values(locAuthorities)

Now use localAuthoritiesArray to draw the circles:

locAuthoritiesArray.forEach(function (la) {
  getcoordinates(la.Postcode).then(function (data) {
    drawcircle(
      data.data.easting,
      data.data.northing,
      la.SchoolCapacity,
      la["LA (name)"],
      la.URN
    );
  });
})
  • Related