"Flatten dictionary" challenge I want to ask for code review for my JavaScript implementation of Flatten_dictionary about this coding challenge for "Flatten a Dictionary" can be found at pramp:


  Given a dictionary dict, write a function flattenDictionary that
  returns a flattened version of it .
  
  If you’re using a compiled language such Java, C++, C#, Swift and Go,
  you may want to use a Map/Dictionary/Hash Table that maps strings
  (keys) to a generic type (e.g. Object in Java, AnyObject in Swift
  etc.) to allow nested dictionaries.
  
  Example:
  
  Input:

dict = {
    "Key1" : "1",
    "Key2" : {
        "a" : "2",
        "b" : "3",
        "c" : {
            "d" : "3",
            "e" : "1"
        }
    }
}

  
  Output:

{
    "Key1" : "1",
    "Key2.a" : "2",
    "Key2.b" : "3",
    "Key2.c.d" : "3",
    "Key2.c.e" : "1"
}

  
  Important: when you concatenate keys, make sure to add the dot character between them. For instance concatenating Key2, c and d the result key would be Key2.c.d.


#implementation#1


function flattenDictionary(dict) {
 //flattened dictionary = {}
  //iterate through dictionary
  //check for type of value for each key
     //if value is another dictionary, i will recurivec callthis appending it to the dictionary
  let flattened = {}
  let keys = Object.keys(dict) //give me an array of keys
  for (let i=0; i&lt; keys.length; i++){
    if (typeof dict[keys[i]] === "object") {
      let flatObj = flattenDictionary(dict[keys[i]])
      let newKeys = Object.keys(flatObj) //[a,b]
      for (let j=0; j&lt;newKeys.length; j++) {
        if (newKeys[j] === "") {
          flattened[keys[i]] = flatObj[newKeys[j]]
        } else {
           flattened[keys[i]+"."+newKeys[j]] = flatObj[newKeys[j]]
        }

      }
      //get back to this
    } else {
      flattened[keys[i]] = dict[keys[i]]
    }
  }
  return flattened
}


#implementation#2

function flattenDictionary(dict) {
  // your code goes here
  let results = {};
  let currKey = "";

  function flattenUtil(dict) {
    // 1. 'Iterate' over the dictionary
    // Keys will either be 'Key_' for outermost level
    // or a letter/empty string for inner levels
    // 2. If the value is an int/string, append onto output dictionary
    // else if the value is a dictionary, recurse
    // 3. Base case: dict empty, return

    Object.keys(dict).forEach(key =&gt; {
      if (typeof dict[key] === 'object') {
        if (!currKey) {
          currKey += key;
        } else {
          currKey += `.${key}`;
        }
        flattenUtil(dict[key]);
      } else {
        if (!currKey) {
          results[`${key}`] = dict[key];
        } else {
          if (!key) {
            results[`${currKey}`] = dict[key];
          } else {
            results[`${currKey}.${key}`] = dict[key];
          }
        }
      }
    });
  }

  flattenUtil(dict);
  return results;
}
One issue I always come across when implementing this myself is that keys can contain .. But your code simply concatenates nested keys together, your flattener function will not correctly represent the object it just flattened. You'll have to find a way to tell a valid . from a . that was part of the key.

While Object.keys() is a really handy function to get the object keys, you're effectively creating an array with it. Same goes for forEach's callback, you're creating the callback function. For larger objects, you easily generate lots of objects this way. This can cause the GC to kick in more frequently, causing things like jerky scrolling, UI freezing, animation hitching. I recommend using plain for-in loops once you start seeing these issues.

Also, your code overcomplicated itself. At every node, your function is generating a flat object, and its return is merged to the parent node's flat object. Theoretically, this is how things are done in a "functional" manner - create, never mutate. But this becomes very impractical, you're building throw-away objects. What you could do is create just one object. Then as you traverse through the tree, just append to it. To the outside world, the function is still pure even if its implementation isn't.

For instance, your code could have been as simple as this (ignoring the . in key issue):



const dict = {
    "Key1" : "1",
    "Key2" : {
        "a" : "2",
        "b" : "3",
        "c" : {
            "d" : "3",
            "e" : "1"
        }
    }
}

const flatten = (source, parentPath = '', target = {}) =&gt; {

  for(const key in source){
    // Construct the necessary pieces of metadata
    const value = source[key]
    const path = parentPath ? `${parentPath}.${key}` : key
    
    // Either append or dive another level
    if (typeof value === 'object'){
      flatten(value, path, target)
    } else {    
      target[path] = source[key]
    }
  }

  return target
}

// To the outside world, you pass in dict, and you get a new object.
// No side effects happen, we're all good.
console.log(flatten(dict))