Home > OS >  Javascript create "Dicitonary-like"-Object out of a list of pathes
Javascript create "Dicitonary-like"-Object out of a list of pathes

Time:12-13

I have a List with multiple pathes like this:

[
"posts/index/cms.md",
"posts/index/test/sidegenerator.md"
"posts/ssg/gatsby.md"
"posts/ssg/hugo.md"
"posts/ssg/generator/rt/gen.txt"
"example/test.md"
]

i want to create a nested dictionary-like structure out of the list, something like this:

{
    "posts": {
        "index": {
            "cms.md": null,
            "test": {
                "sidegenerator.md": null
            }
        },
        "ssg": {
            "gatsby.md": null,
            "hugo.md": null,
            "generator: {
                "rt": {
                    "gen.txt": null
                }
            }
        },
        "example": {
            "test.md": null
        }
    }

}

I came from python and i know something like a dictionary doesnt exist in javascript. Its just an Object, functioning like a dictionary (key:value).

Currently i have these functions:

function create_dict_outof_list(index, rest_of_list){
  if (index == rest_of_list.length-1){
    return rest_of_list[index]
  }else {
    let new_dict = {}
    new_dict[rest_of_list[index]] = create_dict_outof_list(index 1, rest_of_list)
    return new_dict
  }
  
}

function recursive_dict_generator(data_dict, index, list){

  console.log("Dict: ", list[index] in data_dict)
  if (index < list.length-1){
    if (list[index] in data_dict){
      return data_dict[list[index]] = recursive_dict_generator(data_dict[list[index]], index 1, list)

    } else {
      return data_dict[list[index]] = create_dict_outof_list(0, list.slice(index))
    }
  } else {
    if (list[index] in data_dict){

      return data_dict

    } else {
      return list[index]
    }
  }

}

My main where i tested my functions:

function main(){

  let data_dict = {}
  let new_dict = {}
  new_dict = recursive_dict_generator(data_dict, 0, ["posts","index", "test", "sidegenerator.md"])
  console.log("test 1: ", data_dict)
  console.log("test 1.5", new_dict)
  
  recursive_dict_generator(data_dict, 0, ["posts","ssg", "generator", "rt", "test.md"])
  console.log("test 2: ", data_dict)

}
Console log:
test 1:  { posts: { posts: { index: [Object] } } }
test 1.5 { posts: { index: { test: 'sidegenerator.md' } } }
test 2:  { posts: { ssg: { generator: [Object] } } }

I dont know how to handle object-references in Javascript. In this case its not adding a new Key to the existing Dictionary.

CodePudding user response:

First, we just split each path to the sections and then, using recursion, we would fill a dictionary to represent the data:

const paths = [
  "posts/index/cms.md",
  "posts/index/test/sidegenerator.md",
  "posts/ssg/gatsby.md",
  "posts/ssg/hugo.md",
  "posts/ssg/generator/rt/gen.txt",
  "example/test.md"
];

const dictionary = {};

const process = (path, dic) => {
  if (path.length === 0)
    return;
  const key = path.shift();
  if (path.length === 0)
    return dic[key] = null;
  dic[key] ??= {};
  return process(path, dic[key]);
};

paths.forEach(path => process(path.split("/"), dictionary));
console.log(dictionary);


CodePudding user response:

By keeping handy some useful utility functions, we can build such things very simply. Here is a solution using a useful hydrate function, built atop a setPath one:

const expand  = (ss) =>
  hydrate (ss .map (s => [s.split ('/'), null]))

The trick is that hydrate takes an array of values like this:

[
  [['posts', 'index', 'cms.md'], null],
  [['posts', 'index', 'test', 'sidegenerator.md'], null],
  [['posts', 'ssg', 'gatsby.md'], null],
  [['posts', 'ssg', 'hugo.md'], null],
  [['posts', 'ssg', 'generator', 'rt', 'gen.txt'], null],
  [['example', 'test.md'], null]

and turns them into an object. With that handy we only need to write the code to convert our input into that format, which turns out relatively simple.

It turns out that hydrate is similarly simple, built atop setPath, and only that function has any real depth in its five-line implementation.

const setPath = ([p, ...ps]) => (v) => (o) =>
  p == undefined ? v : Object .assign (
    Array .isArray (o) || Number .isInteger (p) ? [] : {},
    {...o, [p]: setPath (ps) (v) ((o || {}) [p])}
  )

const hydrate = (xs) =>
  xs .reduce ((a, [p, v]) => setPath (p) (v) (a), {})

const expand  = (ss) =>
  hydrate (ss .map (s => [s.split ('/'), null]))

const input = ["posts/index/cms.md", "posts/index/test/sidegenerator.md", "posts/ssg/gatsby.md", "posts/ssg/hugo.md", "posts/ssg/generator/rt/gen.txt", "example/test.md"]

console .log (expand (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

  • Related