Wednesday, December 27, 2017

Hiển thị Object kiểu đệ quy

Situation: I have a large object containing multiple sub and sub-sub objects, with properties containing multiple datatypes. For our purposes, this object looks something like this:
var object = {
    aProperty: {
        aSetting1: 1,
        aSetting2: 2,
        aSetting3: 3,
        aSetting4: 4,
        aSetting5: 5
    },
    bProperty: {
        bSetting1: {
            bPropertySubSetting : true
        },
        bSetting2: "bString"
    },
    cProperty: {
        cSetting: "cString"
    }
}
I need to loop through this object and build a list of the keys that shows the hierarchy, so the list ends up looking like this:
aProperty.aSetting1
aProperty.aSetting2
aProperty.aSetting3
aProperty.aSetting4
aProperty.aSetting5
bProperty.bSetting1.bPropertySubSetting
bProperty.bSetting2
cProperty.cSetting
------------------
I made a FIDDLE for you. I am storing a stack string and then output it, if the property is of primitive type:
function iterate(obj, stack) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                    iterate(obj[property], stack + '.' + property);
                } else {
                    console.log(property + "   " + obj[property]);
                    $('#output').append($("<div/>").text(stack + '.' + property))
                }
            }
        }
    }

iterate(object, '') 
I have received a lot of upvotes for this question recently, so I've decided to refine the solution with some ES2015+ magic and more functional style.
It might be less readable, but I like how it looks :) You can still use a simpler solution from above - both of these should work exactly the same.
const isObject = val =>
  typeof val === 'object' && !Array.isArray(val);

const paths = (obj = {}) =>
  Object.entries(obj)
    .reduce(
      (product, [key, value]) =>
        isObject(value) ?
        product.concat([
          [key, paths(value)] // adds [root, [children]] list
        ]) :
        product.concat([key]), // adds [child] list
      []
    )

const addDelimiter = (a, b) =>
  a ? `${a}.${b}` : b;

const pathToString = ([root, children]) =>
  children.map(
    child =>
      Array.isArray(child) ?
      addDelimiter(root, pathToString(child)) :
      addDelimiter(root, child)
  )
  .join('\n');

const input = {
  aProperty: {
    aSetting1: 1,
    aSetting2: 2,
    aSetting3: 3,
    aSetting4: 4,
    aSetting5: 5
  },
  bProperty: {
    bSetting1: {
      bPropertySubSetting: true
    },
    bSetting2: "bString"
  },
  cProperty: {
    cSetting: "cString"
  }
};

// ^ implies a "root" level will be ["", paths(input)]
// ideally paths() should return that structure, but I could not figure out how :)
// shows desired output format
console.log(pathToString(["", paths(input)]));

// showcase the resulting data structure
// any object can be recursively represented as a list [objectPropertyName, [...nestedPropertyNames]]
// console.log(paths(input));

No comments:

Post a Comment