0

I need to traverse this data structure to render a set of lists. With a structure where nested data has the same structure as parent, it would be no problem - but here all models are different. There will be one list where each item contains a list of each level in this structure. Like so:

Desired result:

Unselected

| Countries | Provinces | Cities      | <--- Top ul
---------------------------------------
|-China     |           |             | <--- Sub uls
|-Denmark   |           |             |

Selected

| Countries | Provinces | Cities      |
---------------------------------------
|-China   X |-Matjii X  |- Go Neng    |
|-Denmark   |-Pausiu    |- Tsau Pei X |

The Xs represent a selected option. If an item is selected, only it's information should be shown in subsequent lists. As you see above, since China is selected, the following lists have no data from Denmark shown.

Data structure:

{
  countries: {
    china: {
      name: "China",
      provinces: {
        matjii: {
          name: "Matjii",
          cities: {
            goneng: {
              name: "Go Neng"
            },
            tsauPei: {
              name: "Tsau Pei"
            }
          }
        },
        pausiu: {
          name: "Pausiu",
          cities: {...}
        }
      }
    },
    denmark: {
      name: "Denmark",
      counties: {
        borjskada: {
          name: "Borjskada",
          towns: {
            fjollmaska: {
              name: "Fjollmaska"
            }
          }
        },
        larvmauda: {
          name: "Larvmauda",
          towns: {
            tarnaby: {
              name: "Taernaby"
            }
          }
        }
      }
    }
  }
}

I've tinkered with two different recursive approaches, but what stops me is always that I can not anticipate what the next level might be called (city, town or matjii).

Am I missing some simple obvious solution? Pseudo code is fine, I'm looking for a general approach to this.

5
  • "I can not anticipate what the next level might be called" -- sounds like a case for Object.keys() Commented Aug 21, 2018 at 14:14
  • You should just output whatever is there, regardless of the key name. Meaning you can't display the whole tree on the top level, because you simply don't know which path the user will select. Commented Aug 21, 2018 at 14:37
  • @Jonathan I need the keys to produce an <li> for the top level list, but not all keys. Just some of them (Coutries, Provinces, Cities), and others will not be used for top levels, but rather content in the sub lists (matjii, pausiu). This is why I can't assume what the key means. Commented Aug 21, 2018 at 14:44
  • I would recommend to either fix your data structure or your requirements... Under Denmark there are no Provinces but counties, programmatically you don't want to touch that. Commented Aug 21, 2018 at 14:57
  • adding on to Jonathan's advice. If you have no access to determine the data structure returned by your backend code, you can try normalizing the data structure into a more recursive structure before processing it for UI Commented Aug 21, 2018 at 15:00

1 Answer 1

1

Basically, i believe the key lies in normalizing the data, either from the backend or frontend (js).

In this reply, I would assume you have no choice but to do it from the front end.

Each country have different naming conventions for their geographical locations, Eg. some name it city, counties, towns, village, etc..

Despite the differences in naming, they all follow a tree like hierarchical structure. Hence, this is your angle of attack. Normalize them based on hierarchical level.

The following sample code will print out your sample data in hierarchical level. Their levels are contrasted by the amount of indentation. Copy and paste the following code and run it in your browser console.

Data:

var data = {
  countries: {
    china: {
      name: "China",
      provinces: {
        matjii: {
          name: "Matjii",
          cities: {
            goneng: {
              name: "Go Neng"
            },
            tsauPei: {
              name: "Tsau Pei"
            }
          }
        },
        pausiu: {
          name: "Pausiu",
        }
      }
    },
    denmark: {
      name: "Denmark",
      counties: {
        borjskada: {
          name: "Borjskada",
          towns: {
            fjollmaska: {
              name: "Fjollmaska"
            }
          }
        },
        larvmauda: {
          name: "Larvmauda",
          towns: {
            tarnaby: {
              name: "Taernaby"
            }
          }
        }
      }
    }
  }
}

Functions:

function traverseDataStructure(rootNode, nodeLevel){

    if(!rootNode) return;

    if(nodeLevel > 3) return;  //just a secondary protection for infinite recursion

    if(!nodeLevel)
        nodeLevel = 0;

    var indentationForPrinting = '';
    for(var i=0;i<nodeLevel;i++){
        indentationForPrinting += '    ';
    }

    console.log(indentationForPrinting + 'Traversing level ' + nodeLevel);

    for(var prop in rootNode){

        //prop may either by countries (level 0) or provinces (level 1) or counties (level 1) or cities (level 2) or towns (level 2)
        var regionType = prop;
        var regionList = getRegionList(rootNode[prop]);
        console.log(indentationForPrinting + 'regionType: ' + regionType);
        console.log(indentationForPrinting + 'regionList: ' + regionList);

        if(regionList.length <= 0) return;

        //then iterate the list of regions
        for(var i=0;i<regionList.length; i++){

            for(var subRegion in regionList[i]){
                if (subRegion == 'name'){
                    var regionName = regionList[i][subRegion];
                    console.log(indentationForPrinting + 'regionName: ' + regionName);
                } else {
                    traverseDataStructure({subRegion: regionList[i][subRegion]}, nodeLevel+1);
                }
            }
        }
    }
}

function getRegionList(obj){
    var list = [];
    for(var location in obj){
        list.push(obj[location]);
    }
    return list;
}

Run it:

traverseDataStructure(data);

Expected output:

enter image description here

Hope this can give you an alternative to tackle your problem

Sign up to request clarification or add additional context in comments.

5 Comments

Looks like a sound approach! To render the menu structure, I'll need some way to determine which data should be used to create the top level <li> elements (Countries, Provinces/Counties, Cities/Towns). To me it seems hard to programatically pull that info from this structure. What do you say?
Whether at the db level or ui level, You should be using the same terminology, Eg: Countries > Provinces > Cities. Counties will be treated as provinces and towns will be treated like Cities. The code snippet I gave you iterates all levels of your current data structure.You can slightly modify it to rebuild a sanitized version of your data-structure / view-model
With reference to the data variable above, another way to "cheat" is to make use of JSON.stringify(data) and JSON.parse(stringifiedJSON); When you get a json string from your datastructure, you can string replace all instances of counties for provinces, and likewise cities for towns. then JSON.parse this string back into your data-structure. In that way you do not need to deal with any naming differences.
I get what you're saying, I can not normalise the labels though. The cities has to have a label 'Cities' and the towns 'Towns' etc. This is the nature of my problem really.
Then, you can do what I am doing in my code sample, create a recursive data structure, with 2 fields, RegionType (country, city, county, whatever it maybe), and RegionList(a list of data structure for children regions). Given your strict requirements, there will not be a straight forward way out. I think you just need to bite the bullet and get it done, lol

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.