2

Have an object shown below were I need to iterate over each object property to find nextStep and push to an array. My output should have a single array variable with all "nextStep" properties.

Input:

{
  "Product1": {
    "stepName": "step1",
    "stepOutputStatus": "normal",
    "nextStep": {
      "stepName": "step2",
      "stepOutputStatus": "normal",
      "nextStep": {
        "stepName": "step3",
        "stepOutputStatus": "warning",
        "nextStep": {
          "stepName": "step4",
          "stepOutputStatus": "warning",
          "nextStep": null
        }
      }
    }
  }
}

Expected Output:

[
  {
    "stepName": "step2",
    "stepOutputStatus": "normal"
  },
  {
    "stepName": "step3",
    "stepOutputStatus": "warning"
  },
  {
    "stepName": "step4",
    "stepOutputStatus": "warning"
  }
]

I tried below code, but it returns null due to scoping issue:

function iterObj(obj) {
  var result = [];
  for (var key in obj) {
    if (
      obj[key] !== null &&
      typeof obj[key] === "object" &&
      key == "nextStep"
    ) {
      var data = this.iterObj(obj[key]);
      result.push(data);
    }
  }
  return result;
}

iterObj(obj);
chazsolo
  • 7,873
  • 1
  • 20
  • 44

4 Answers4

3

You can iterate using a JavaScript generator (without using recursion).
Just step down to the next step until it's not defined.

If you are unfamiliar with function *, please refer to MDN documentation.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

const product = {
  stepName: "step1",
  stepOutputStatus: "normal",
  nextStep: {
    stepName: "step2",
    stepOutputStatus: "normal",
    nextStep: {
      stepName: "step3",
      stepOutputStatus: "warning",
      nextStep: {
        stepName: "step4",
        stepOutputStatus: "warning",
        nextStep: null
      }
    }
  }
};

function* iterObj(obj) {
  while (obj.nextStep) {
    const { stepName, stepOutputStatus } = obj;
    yield { stepName, stepOutputStatus };
    obj = obj.nextStep;
  }
}

const iterator = iterObj(product);
console.log(Array.from(iterator));
dance2die
  • 35,807
  • 39
  • 131
  • 194
  • 1
    As an alternative one could also just push `obj` to an array in the loop and return it at the end ... however the generator is also a good fit here ... – Jonas Wilms Mar 14 '19 at 14:27
  • I was using a recursion but all of a sudden it looked hard to read. The generator idea just popped up in my out of nowhere – dance2die Mar 14 '19 at 14:34
  • well, in this case a simple `while` loop is just enough, thats the primary reason I upvoted ... the generator is just syntactic sugar in this case... – Jonas Wilms Mar 14 '19 at 14:42
  • Thanks @JonasWilms. I also found kemikofa's [answer](https://stackoverflow.com/a/55165041/4035) an elegant way to recurse over. – dance2die Mar 14 '19 at 14:54
  • 1
    @SungM.Kim I admit that the iterator is very smart. I never thought to combine a function generator with Array.from. – kemicofa ghost Mar 14 '19 at 15:04
  • Thank you @kemicofa. I looked up SO for that one ;) https://stackoverflow.com/a/28718967/4035 – dance2die Mar 14 '19 at 17:02
2

You can do this recursively with spread syntax and destructuring.

const data={"Product1":{"stepName":"step1","stepOutputStatus":"normal","nextStep":{"stepName":"step2","stepOutputStatus":"normal","nextStep":{"stepName":"step3","stepOutputStatus":"warning","nextStep":{"stepName":"step4","stepOutputStatus":"warning","nextStep":null}}}}}

function handleData({nextStep, ...rest}){
  const res = [];
  res.push(rest);
  if(nextStep){
     res.push(...handleData(nextStep));
  }
  return res;
}

const res = handleData(data.Product1);

console.log(res);

More compact version:

const data={"Product1":{"stepName":"step1","stepOutputStatus":"normal","nextStep":{"stepName":"step2","stepOutputStatus":"normal","nextStep":{"stepName":"step3","stepOutputStatus":"warning","nextStep":{"stepName":"step4","stepOutputStatus":"warning","nextStep":null}}}}}

const handleData = ({nextStep, ...rest}) => [rest].concat(nextStep ? handleData(nextStep) : []);

const res = handleData(data.Product1);

console.log(res);
kemicofa ghost
  • 16,349
  • 8
  • 82
  • 131
0

Recursive function that will copy every key that is not matching the given one which is used to go deeper.

const obj = {
  "Product1": {
    "stepName": "step1",
    "stepOutputStatus": "normal",
    "nextStep": {
      "stepName": "step2",
      "stepOutputStatus": "normal",
      "nextStep": {
        "stepName": "step3",
        "stepOutputStatus": "warning",
        "nextStep": {
          "stepName": "step4",
          "stepOutputStatus": "warning",
          "nextStep": null
        }
      }
    }
  }
};

function getDataBehindKey(key, ptr) {
  if (!ptr) {
    return [];
  }

  return Object.keys(ptr).reduce((tmp, x) => {
    if (x === key) {
      return [
        ...tmp,
        ...getDataBehindKey(key, ptr[x]),
      ];
    }

    tmp[0][x] = ptr[x];

    return tmp;
  }, [{}]);
}

console.log(getDataBehindKey('nextStep', obj.Product1));
Orelsanpls
  • 22,456
  • 6
  • 42
  • 69
0

let obj={
  "Product1": {
    "stepName": "step1",
    "stepOutputStatus": "normal",
    "nextStep": {
      "stepName": "step2",
      "stepOutputStatus": "normal",
      "nextStep": {
        "stepName": "step3",
        "stepOutputStatus": "warning",
        "nextStep": {
          "stepName": "step4",
          "stepOutputStatus": "warning",
          "nextStep": null
        }
      }
    }
  }
}

let output=[];


function iterObj(obj) {
  while(obj.nextStep!=null && obj.hasOwnProperty('nextStep')){
    getNextStep(obj.nextStep);
    obj=obj.nextStep;
  }
}

function getNextStep(object){
  if(object.hasOwnProperty('nextStep')){
     var data = {stepName:object.stepName,stepOutputStatus:object.stepOutputStatus};
     output.push(data);
  }
}

iterObj(obj["Product1"]);
console.log(output);
Saurabh Mistry
  • 12,833
  • 5
  • 50
  • 71