0

I'm trying to get my head around NodeJS by writing a few test apps. I've managed okay so far but the one thing that's tripping me up is the following.

I've got it reading a directory for folders, and then parsing the settings.json files within each folder (so I can build a UI on the fly based on the folder contents). The problem is, I can't seem to "step down a level" with the JSON object.

For example, coming from a background in php where you could do something like the following to 'step' through the array:

<?php
    $arr = [
        'folder1' => 
            [ 'name' => 'test',
              'icon' => 'icon-test'
            ],
        'folder2' => 
            [ 'name' => 'test-2',
              'icon' => 'icon-test-2'
            ]
    ];

    foreach($arr as $k => $v){
        // folder1 level
        foreach($v as $k2 => $v2){
                // name / icon level.
                echo $k2 . ' is ' . $v2;
        }
    }
?>

Is there an equivalent in JS? I can do the first "level" by doing this:

function getDirectories (srcpath) {
  return fs.readdirSync(srcpath)
    .filter(file => fs.lstatSync(path.join(srcpath, file)).isDirectory())
}
var d = getDirectories('modules');
var modules = {};

// the following reads the json in each dir into an object like:
// { 'dirname': { 'key1': 'val1', 'key2': 'val2' }, 'dirname2'... }
for(var p in d){
    modules[d[p]] = JSON.parse(fs.readFileSync('./modules/'+d[p]+'/config.json', 'utf8'));
}

for(var k in modules){
    console.log(k);
}

But ideally I need to extract "name" and "icon" from the JSON. I can't seem to find any way of doing this.

I understand this is probably messy but I'm just getting my head around NodeJS. For full clarity, directory structure and my simple JSON files below:

modules directory structure

modules
|____ test
      |____ config.json
|____ test-2
      |____ config.json

config.json (example)

{
  "name": "test",
  "icon": "test-icon"
}
3
  • I would take a look at lodash. It is available for node.js, it is lightweight, and IMO is a very good utility library for javascript. It has collection methods that will iterate an object just as you are asking. Commented Jun 23, 2017 at 15:44
  • 1
    whats your question? the upper code works perfectly? except that youre using an for..in loop over an array Commented Jun 23, 2017 at 15:45
  • @Jonasw I've clarified my question but if you re-read it, it does say I'm looking to step through the JSON object - further than I can already. Commented Jun 23, 2017 at 15:48

5 Answers 5

2
for(var module of modules){
   console.log(module.name,module.icon);
   //or over all
   for(var value of module) console.log(value);
}

The for...of loops over values. With for..in (looping over keys) :

for(var name in modules){
  var module=modules[name];
  for(key in module){
    console.log(name+"/"+key+":"+module[key]);
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

This seems to break for me citing modules[Symbol.iterator] is not a function any idea?
Scratch that - this did exactly what it was intended to.
1

You're currently using a for in loop, and while that works, when iterating over arrays you ideally want to use for next loops.

Here is an example:

for (var i = 0; i < folders.length; i++) {
    var folder = folders[i]

    /* work with directory .. */

    for (var j = 0; j < folder.items.length; j++){
        var item = folder.items[j]

        /* work with item.. */
    }
}

The reason for not using for in when iterating over array is because an array can contain objects that aren't actually part of the array, i.e a function.

Using a for in loop, you will iterate over the array contains, and then any object/function attached to that array.

for next loops will only iterate over the array contents.

5 Comments

Is it an array though? It's still a JSON object when I read it (using JSON.parse)
A JSON object can be a collection of arrays and objects, JSON.parse can handle both.
I did not know that - I thought it was a mutually exclusive thing (objects or arrays with no crossover). Thanks!
@ScottMcGready its not important for the json thing, however, you might edit your p in d loop
Unfortunately it seems folders.length is undefined?
0

I don't know do you have only 1 subdirectory or can that be multiple? If it can be any depth use a recursive function like:

function iterateFolder(array) {
  for (var i = 0; i < array.length; i++) {
    if ($.isArray(array[i]))
      iterateFolder(array[i]);
    else
      //Add to global variable for example or parse just the values
  }
}

4 Comments

Theoretically it should never go "deeper" than described above. But I'm looking for a way to learn how to iterate over directories / contents regardless of depth. This would certainly fit that description!
Then it would make sense using a recursive function. Check if the element is a folder, then call the function again with the new folder. If it is no folder you can get the element content. $.isArray(array[i]) is jQuery syntax!
@schlumpf jquery on nodejs?? are you kidding? why not Array.isArray([1,2,3])
@Jonas Oh yeah when I was writing that I haven't in mind anymore that he is using nodejs. But that code was only to illustrate that he can use a recursive function to access any depth. It is no solution anyway that he can use without adjusting the code. But yes Array.isArray would be the correct syntax so far
0

try doing a for of loop and not a for in loop

            // example from a old proj
            // your code has in 
            // if you are using es6 syntax, you should use let instead of var
            for (let element of section.elements) {
                let $element;
                $element = $parent.find(`label[for=${element._data_text}]`);                    
                $element.html(`<span>${element[lang]}</span>`);
            }

here is a working example with my json

import lang_data from "../data/languages.json";
$(function () {
    console.log("onload hit");
    $("[data-language]").on("click", function () {
        let $this = $(this);
        let data = $this.data();
        let lang = data.language;
        $("[data-language]").removeClass("active");
        $this.addClass("active");
        switchLanguage(lang);
        return true;
    });
})

function switchLanguage(lang) {
    for (let section of lang_data.sections) {
        // the form is loaded by JS. I could not add a data-text to it.
        if (section.id === "form1_handle_after") {
            let $parent = $("#form1");
            for (let element of section.elements) {
                let $element;
                if (element._data_text == "c-submit-button") {                    
                    $element = $parent.find(`#${element._data_text}`);                    
                } else {
                    $element = $parent.find(`label[for=${element._data_text}]`);                    
                }
                $element.html(`<span>${element[lang]}</span>`);
            }
        } else {
            let $parent = $(`#${section.id}`);
            for (let element of section.elements) {
                let $element = $parent.find(`[data-text=${element._data_text}]`);
                // text needs to be highlighted
                if(element.mark !== null){                    
                    let string = element[lang];                    
                    let find = element.mark[lang];
                    let new_string = string.replace(find, `<mark>${find}</mark>`)
                    // I assume it is wrapped in quotes
                    $element.html(`<span>&ldquo;${new_string}&rdquo;</span>`);
                }else{
                    $element.html(`<span>${element[lang]}</span>`);
                }

            }
        }

    }
}

Here is a sample of my data

{
"sections": [
    {
        "id": "navbar_main",
        "elements": [
            {
                "_data_text": "question",
                "eng": "Have a Question?",
                "chi": "有个问题?",
                "mark": null
            }
        ]
    }
  ]
}

You can view the html and markup at http://professional.ongeo.msu.edu/

1 Comment

I added a full example she/he or anyone else in the future can reference if they do not fully understand JS yet.
0

I know you are starting with node, but i guess you should use "reduce" instead of those "for loops" for this case (it's just my personal opinion xd). You can find many guides of how to use this function, you variable "d" is an array and all arrays has the "reduce" function.

Something like this:

const modules = d.reduce((all, current) => {
  all[current] = current || {}
  all[current] = JSON.parse(fs.readFileSync(__dirname + '/modules/'+ current +'/config.json', 'utf8'))
  return all;
}, {})

I test it creating some folders and gave me this:

​​​​​{ test: { name: 'test', icon: 'test-icon' },​​​​​ ​​​tes_2: { name: 'test-2', icon: 'test-icon-2' } }​​​​​

Comments

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.