I'm refactoring some code, essentially a dot-notation walker for objects. I've abstracted things quite a bit, I'm to the point any more and I begin to loose readability.It all hinges on this one function objectWalker, which does the actual object traversals. I can control what happens by passing in different callbacks to handle key points in the processes. function objectWalker (paths, obj, onEnd, enRoute, onEach) {      idx = -1,      nextContext = prevContext = obj;      const len = paths ? paths.length : 0;      const lastStep = len ? len - 1 : 0;      while (nextContext != null && ++idx < len) {        if (onEach) nextContext = onEach(nextContext, idx, paths, prevContext);        if (idx !== lastStep) {          prevContext = nextContext;          nextContext = (!dotWalker.objectHas(nextContext, paths[idx])) ? enRoute(nextContext, idx, paths, prevContext) : nextContext[paths[idx]];        } else {          prevContext = paths.length === 1 ? nextContext : prevContext[paths[idx - 1]];          return onEnd(nextContext, idx, paths, prevContext );        }        if(!nextContext) return nextContext;      }      return nextContext;    }Pretty simple, and here are some of the callbacks i can send in.dotWalker.onEnd = function(context, idx, dotPathss, prevContext) {  if (idx === 0) {    return dotWalker.objectHas(context, dotPathss[idx]) ? context[dotPathss[idx]] : undefined;  } else {    return prevContext[dotPathss[idx]];  }}dotWalker.onEndAssigner = function(value){  return function(context, idx, dotPathss, prevContext) {    return prevContext[dotPathss[idx]] = value, prevContext[dotPathss[idx]];  }}dotWalker.enRouteMapper = function(context, idx, dotPathss, prevContext) {    return context[dotPathss[idx]] = {}, context[dotPathss[idx]];}dotWalker.enRoute = function(context, idx, dotPathss, prevContext) {  return dotWalker.objectHas(context, dotPathss[idx]) ? context[dotPathss[idx]] : undefined ;}dotWalker.onEach = function(context, idx, dotPaths, prevContext) {  return context;}I'm pretty pleased with the above, the issue i have is with the following pathFinder function. It seems to me to be doing too much, but I can't decide where to break it down and refactor further.     dotWalker.pathFinder = function(paths, c, namespace, pd, nd) {  if (!c && !paths) return [];  pd = pd || '.';  nd = nd || ':';  const lastChar = paths[paths.length - 1];  const isNamespacedDotPath = paths.indexOf(nd) > -1;  if ( !!dotWalker.objectHas(c, 'ns') ) {    if ( lastChar === nd ) { // label: -> namespaced but no dotPaths      namespace = c[paths.slice(0, -1)], paths = undefined;    }     if ( isNamespacedDotPath ) { // label:some.sub.path -> namespaced but with dotPaths      paths = paths.split(nd), namespace = c.ns[paths[0]], paths = paths.slice(1)[0].split(pd);    }    if(!namespace){      paths = paths.split(pd), namespace = c.selected || c.ns.root;    }  } else { /* Attempting to use namespaceses on simple object */    if ( isNamespacedDotPath ) {      paths = paths.split(nd);      if( !c || !dotWalker.objectHas(c, paths[0])){         namespace = undefined, paths = [];      } else {         namespace = c[paths[0]], paths = paths.slice(1).join('.').split(pd);      }    }    if (typeof paths === 'string' && paths.indexOf(pd) > -1) {      paths = paths.split(pd);    }    if ( lastChar === nd){      paths = '';    }    namespace = namespace || c;  }  paths = paths[0] === '' || !paths ? undefined : [].concat(paths);  return !c ? paths : { dotPaths: paths, context: namespace };}Any feedback is welcome, thanks.I've included the full code here in a little snippet, you can run it to see how it works. I've also included a crude suite of tests, to highlight intended behavior.I've added two more functions to demonstrate how one might leverage the objectWalker.dotWalker.assign = function (dotPath, value, context) {  return dotWalker(dotPath, context, dotWalker.onEndAssigner(value), dotWalker.enRouteMapper);}dotWalker.get = function (dotPath, context) {  return dotWalker(dotPath, context, dotWalker.onEnd, dotWalker.enRoute);}And I added two more tests demonstrating their use.  /**        DotWalker accepts a path a context/object and two callbacks.         The path, can be a dotPaths, as the context, is being walked  */  const dotWalker = (function() {    const dotWalker = function (dotPaths, context, end, enRoute, onEach){      end = end || dotWalker.onEnd;       enRoute = enRoute || dotWalker.enRoute;      var { context, dotPaths } = dotWalker.pathFinder( dotPaths || '', context);      return objectWalker(dotPaths, context, end, enRoute, onEach);    }    dotWalker.assign = function (dotPath, value, context) {      return dotWalker(dotPath, context, dotWalker.onEndAssigner(value), dotWalker.enRouteMapper);    }    dotWalker.pathFinder = function(paths, c, namespace, pd, nd) {      if (!c && !paths) return [];      pd = pd || '.';      nd = nd || ':';      const lastChar = paths[paths.length - 1];      const isNamespacedDotPath = paths.indexOf(nd) > -1;      if ( !!dotWalker.objectHas(c, 'ns') ) {        if ( lastChar === nd ) { // label: -> namespaced but no dotPaths          namespace = c[paths.slice(0, -1)], paths = undefined;        }         if ( isNamespacedDotPath ) { // label:some.sub.path -> namespaced but with dotPaths          paths = paths.split(nd), namespace = c.ns[paths[0]], paths = paths.slice(1)[0].split(pd);        }        if(!namespace){          paths = paths.split(pd), namespace = c.selected || c.ns.root;        }      } else { /* Attempting to use namespaceses on simple object */        if ( isNamespacedDotPath ) {          paths = paths.split(nd);          if( !c || !dotWalker.objectHas(c, paths[0])){             namespace = undefined, paths = [];          } else {             namespace = c[paths[0]], paths = paths.slice(1).join('.').split(pd);          }        }        if (typeof paths === 'string' && paths.indexOf(pd) > -1) {          paths = paths.split(pd);        }        if ( lastChar === nd){          paths = '';        }        namespace = namespace || c;      }      paths = paths[0] === '' || !paths ? undefined : [].concat(paths);      return !c ? paths : { dotPaths: paths, context: namespace };    }    function objectWalker (paths, obj, onEnd, enRoute, onEach) {      idx = -1,      nextContext = prevContext = obj;      const len = paths ? paths.length : 0;      const lastStep = len ? len - 1 : 0;      while (nextContext != null && ++idx < len) {        if (onEach) nextContext = onEach(nextContext, idx, paths, prevContext);        if (idx !== lastStep) {          prevContext = nextContext;          nextContext = (!dotWalker.objectHas(nextContext, paths[idx])) ? enRoute(nextContext, idx, paths, prevContext) : nextContext[paths[idx]];        } else {          prevContext = paths.length === 1 ? nextContext : prevContext[paths[idx - 1]];          return onEnd(nextContext, idx, paths, prevContext );        }        if(!nextContext) return nextContext;      }      return nextContext;    }    dotWalker.onEnd = function(context, idx, dotPathss, prevContext) {      if (idx === 0) {        return dotWalker.objectHas(context, dotPathss[idx]) ? context[dotPathss[idx]] : undefined;      } else {        return prevContext[dotPathss[idx]];      }    }    dotWalker.onEndAssigner = function(value){      return function(context, idx, dotPathss, prevContext) {        return prevContext[dotPathss[idx]] = value, prevContext[dotPathss[idx]];      }    }dotWalker.assign = function (dotPath, value, context) {  return dotWalker(dotPath, context, dotWalker.onEndAssigner(value), dotWalker.enRouteMapper);}dotWalker.get = function (dotPath, context) {  return dotWalker(dotPath, context, dotWalker.onEnd, dotWalker.enRoute);}    dotWalker.enRouteMapper = function(context, idx, dotPathss, prevContext) {        return context[dotPathss[idx]] = {}, context[dotPathss[idx]];    }    dotWalker.enRoute = function(context, idx, dotPathss, prevContext) {      return dotWalker.objectHas(context, dotPathss[idx]) ? context[dotPathss[idx]] : undefined ;    }    dotWalker.onEach = function(context, idx, dotPaths, prevContext) {      return context;    }    dotWalker.objectHas = function(obj, prop) {      return obj && Object.prototype.hasOwnProperty.call(obj, prop);    }    dotWalker.isObject = function(value) {      const type = typeof value      return value != null && (type == 'object' || type == 'function')    };    return dotWalker;  })();                /////// EXAMPLES AND TESTS          console.clear();  var tcontext = {};  var tmp;  console.log('Testing pathFinder functionalities')  console.log('_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _')    tmp = dotWalker.pathFinder('some.path');  console.log(    'returns an array of paths, if no context is provided.',     (tmp.length !== 0)  );    tmp = dotWalker.pathFinder();  console.log(    'returns an empty set if no context or dotPaths is supplied.',     (tmp.length === 0)  );    tmp = dotWalker.pathFinder('some.path', { name: 'empty' });  console.log(    'returns a context object like { context:..., dotPaths:...}',     !!(tmp.context && tmp.dotPaths)  );    tmp = dotWalker.pathFinder('app:person.name');  console.log(    'returns undefined when no context is supplied.',     (tmp.context === undefined)  );    tmp = dotWalker.pathFinder('app:person.name');  console.log(    'returns undefined when no matching namespace exists.',     (tmp.context === undefined)  );    tmp = dotWalker.pathFinder('app:', { app: true });  console.log(    'returns a context based on namespace when one is specified in the path.',     (tmp.context === true)  );    tmp = dotWalker.pathFinder('app:', { app: true });  console.log(    'returns a context object with an empty dotPaths when none provided',    'following a namespace.',     (tmp.context === true)  );    tmp = dotWalker.pathFinder('name', { name: 'John Doe' });  console.log(    'can use dotPathss for simple objects.',     (tmp.context.name === 'John Doe')  );    tmp = dotWalker.pathFinder('name', { ns: { root: { name: 'John Doe' } } });  console.log(    'will use the root namespace if none is specified.',     (tmp.context.name === 'John Doe')  );    tmp = dotWalker.pathFinder('app:name', { ns: { root: { name: 'John Doe' }, app: { name: 'App Name' } } });  console.log(    'will use namespaces to resolve values when supplied.',     (tmp.context.name === App Name)  );  tmp = dotWalker.pathFinder('app:prop.that.does.not.exist', { ns: { root: { name: 'John Doe' }, app: { name: 'App Name' } } });  console.log(    'will return a valid namespace for paths that may not exist',     (tmp.context.name === App Name)  );    console.log('- - - - - - - - - - - - - - - - - - - - - - - - - -')  console.log('Testing dotWalker functionalities')  console.log('_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _')    tmp = dotWalker();  console.log(    'will return undefined, if no dotPaths or context is provided',     (tmp === undefined)  );    tmp = dotWalker('some.path');  console.log(    'will return undefined, if no context is provided',     (tmp === undefined)  );    tmp = dotWalker(undefined, {    name: 'app name'  });  console.log(    'will return context, if no dotPaths is provided',     (tmp.name === app name)  );    tmp = dotWalker('', {    name: 'app name'  });  console.log(    'will return context, if dotPaths is an empty string',     (tmp.name === app name)  );    tmp = dotWalker('name', { name: 'app name' });  console.log(    'With a simple object, without any supplied namespace, will ',    'return a prop that matches the dotPaths',     (tmp === app name)  );  var tmp = dotWalker('name.firstName', { name: {firstName: 'John Doe'}});  console.log(    'With a simple object, will retrieve a value of a nested dotPathss',     (tmp === 'John Doe')  );    // Namespaces   console.log('Namespaces...............');    tmp = dotWalker('name:', { name: 'app name' });  console.log(    'With a simple object, where a namespace was provided ',     'and the dotPaths was undefined will return a prop value ',    'that matches the namespace.',     (tmp === app name)  );    tmp = dotWalker('name:firstName', {name: {firstName: 'John Doe'}  }, dotWalker.onEnd, dotWalker.enRoute);  console.log(    'With a simple object, will retrieve a value of a dotPathss ',    'when using namespaces',     (tmp === 'John Doe')  );    tmp = dotWalker('name:firstName.english.two',{ name: {firstName: { english: { two: 'John Doe'}}}});  console.log(    'With a simple object, will retrieve a value of a nested ',     'dotPaths, when using namespaces',     (tmp === John Doe)  );    tmp = dotWalker('name:firstName.english.four', { name: {firstName: { english: { two: 'John Doe'}}}});  console.log(    'With a simple object, will return undefined for an invalid path ',     'dotPaths, when using namespaces',     ( tmp === undefined )  );    tmp = dotWalker('name:firstName.apple.four', { name: {firstName: { english: { two: 'John Doe'}}}});  console.log(    'With a simple object, will return undefined for an invalid path ',     'dotPaths, when using namespaces',     ( tmp === undefined )  );  // Assignments  console.log('Simple Object Context Assignments...............');  tcontext = {};  tmp = dotWalker(    'name',     tcontext,    dotWalker.onEndAssigner('Sally Sue'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values to an object',     ( tmp === tcontext.name )  );  tcontext = {name: {}};  tmp = dotWalker(    'name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign a value using dotPathss',     ( tmp === tcontext.name.firstName )  );  tcontext = {name: {}};  tmp = dotWalker(    'person.name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values using nested dotPathss, and it will fill in the missing links',     ( tmp === tcontext.person.name.firstName )  );  tcontext = {we:{}};  tmp = dotWalker(    'we:person.name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values using nested dotPathss and namespaces, and it will fill in the missing links',     ( tcontext.we.person.name.firstName === tmp )  ); // Assignments  console.log('Namespaced Context Assignments...............');  tcontext = {ns:{}};  tmp = dotWalker(    'name',     tcontext,    dotWalker.onEndAssigner('Sally Sue'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values to an object',     ( tmp === tcontext.name )  );  tcontext = {name: {}};  tmp = dotWalker(    'name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign a value using dotPathss',     ( tmp === tcontext.name.firstName )  );  tcontext = {name: {}};  tmp = dotWalker(    'person.name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values using nested dotPathss, and it will fill in the missing links',     ( tmp === tcontext.person.name.firstName )  );  tcontext = {we:{}};  tmp = dotWalker(    'we:person.name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'Can assign values using nested dotPathss and namespaces, and it will fill in the missing links',     ( tcontext.we.person.name.firstName === tmp )  );  tcontext = undefined;  tmp = dotWalker(    'we:person.name.firstName',     tcontext,    dotWalker.onEndAssigner('Sally'),     dotWalker.enRouteMapper  );  console.log(    'returns undefined when no context is given',     ( tmp === undefined )  );  tcontext = {we:{}};  tmp = dotWalker(    'we:person.name.firstName',     tcontext,    dotWalker.onEnd,     dotWalker.enRoute  );  console.log(    'returns undefined when no value exists at path',     ( tmp === undefined )  );    tcontext = {we:{name: John Doe}};  tmp = dotWalker.get(    'we:name.firstName',    tcontext  );  console.log(    'follows a dotPath, and returns a value or undefined.',     ( tmp === undefined )  );  // Accessor Methods  console.log('Accessors...............');  tcontext = {we:{name:{}}};  tmp = dotWalker.assign(    'we:name.firstName',    'John',    tcontext  );  tmp = dotWalker.assign(    'we:name.lastName',    'Doe',    tcontext  );  console.log(    'assigns a value using onEndAssigner and enRouteMapper',     ( tcontext.we.name.firstName === John )  );  tmp = dotWalker.get(    'we:name.firstName',    tcontext  );  console.log(    'follows a dotPath, and returns a value or undefined.',     ( tmp === John )  );  console.log('_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _')
Refactoring an Object Walker utility function in Javascript
javascript;functional programming