JavaScript Typescript function using recursion and yield keyword to pull out nested lists typescript function in interface,typescript function in class,typescript function

I'm trying to rewrite a C# function that uses yield and recursion to pull out instances of a class out of nested lists into a single list.

This is the C# function:

 public static IEnumerable<TargetObject> GetRecursively(params TargetObject[] startingObjects)
 {
    foreach (TargetObject startingObject in startingObjects)
    {
        yield return startingObject;
        if (startingObject.InnerObjects != null)
            foreach (TargetObject innerObject in startingObject.InnerObjects.ToArray())
                foreach (TargetObject recursiveInner in GetRecursively(innerObject))
                    yield return recursiveInner;
     }
 }

Being that javascript doesn't reliably support yield across browsers, how can I simulate it in this complex function?

function getRecursively(...startingObjects: TargetObject[])
{
    return function () {
           ??
    }
}
Answer:1

If you want to run some code for each of the items, you can pass in the callback to be executed. This allows similar behaviour to the iterator pattern as the looping will pause while the callback is executed, and then continue once the callback finishes.

This is useful if the iteration itself is obtaining data dynamically that you don't want to hold in memory for the whole process or if you want to avoid flattening the array.

This isn't the same as using yield in C# but it is just as simple - all you need to do is write a function that will run the code against each item found.

Here is an example:

class TargetObject {
    constructor(public name: string, public innerObjects: TargetObject[]) {
    }

    static forEachRecursive(callback: (item: TargetObject) => any, targetObjects: TargetObject[]){
        for (var i = 0; i < targetObjects.length; i++) {
            var item = targetObjects[i];

            callback(item);

            if (item.innerObjects) {
                TargetObject.forEachRecursive(callback, item.innerObjects);
            }
        }
    }
}

var targetObjects = [
    new TargetObject('Example', []),
    new TargetObject('With Inner', [
        new TargetObject('Inner 1', []),
        new TargetObject('Inner 2', [])
    ])
];

var callback = function (item: TargetObject) {
    console.log(item.name);
};

TargetObject.forEachRecursive(callback, targetObjects);
Answer:2

The way the yield keyword works internally is by creating a state machine. You can create one yourself, or if the list is not too large and you can reasonably keep it in memory you can simply return and use a list e.g:

function getRecursively(...startingObjects:TargetObject[] ):TargetObject[] 
 {
    var toreturn = [];
    for (var key in startingObjects)
    {
        var startingObject = startingObjects[key];
        toreturn.push(startingObject);
        if (startingObject.InnerObjects != null)
            for (var key2 in startingObject.InnerObjects){
                var innerObject = startingObject.InnerObjects[key2];
                var superInner = getRecursively(innerObject);
                for (var key3 in superInner)
                    toreturn.push(superInner[key3]);                        
            }
     }
    return toreturn; 
 }

If you really really want yield you could use the google traceur compiler : https://github.com/google/traceur-compiler e.g.

function* cool() {
  yield 123;    
  yield 456;  
}

for (n of cool()) {    
    console.log(n);
}  

Try it online

As you can see the generated state machine is not trivial.

var $__generatorWrap = function(generator) {
  return $traceurRuntime.addIterator({
    next: function(x) {
      switch (generator.GState) {
        case 1:
          throw new Error('"next" on executing generator');
        case 3:
          throw new Error('"next" on closed generator');
        case 0:
          if (x !== undefined) {
            throw new TypeError('Sent value to newborn generator');
          }
        case 2:
          generator.GState = 1;
          if (generator.moveNext(x, 0)) {
            generator.GState = 2;
            return {
              value: generator.current,
              done: false
            };
          }
          generator.GState = 3;
          return {
            value: generator.yieldReturn,
            done: true
          };
      }
    },
    'throw': function(x) {
      switch (generator.GState) {
        case 1:
          throw new Error('"throw" on executing generator');
        case 3:
          throw new Error('"throw" on closed generator');
        case 0:
          generator.GState = 3;
          throw x;
        case 2:
          generator.GState = 1;
          if (generator.moveNext(x, 1)) {
            generator.GState = 2;
            return {
              value: generator.current,
              done: false
            };
          }
          generator.GState = 3;
          return {
            value: generator.yieldReturn,
            done: true
          };
      }
    }
  });
};
function cool() {
  var $that = this;
  var $arguments = arguments;
  var $state = 0;
  var $storedException;
  var $finallyFallThrough;
  var $G = {
    GState: 0,
    current: undefined,
    yieldReturn: undefined,
    innerFunction: function($yieldSent, $yieldAction) {
      while (true) switch ($state) {
        case 0:
          this.current = 123;
          $state = 1;
          return true;
        case 1:
          if ($yieldAction == 1) {
            $yieldAction = 0;
            throw $yieldSent;
          }
          $state = 3;
          break;
        case 3:
          this.current = 456;
          $state = 5;
          return true;
        case 5:
          if ($yieldAction == 1) {
            $yieldAction = 0;
            throw $yieldSent;
          }
          $state = 7;
          break;
        case 7:
          $state = -2;
        case -2:
          return false;
        case -3:
          throw $storedException;
        default:
          throw "traceur compiler bug: invalid state in state machine: " + $state;
      }
    },
    moveNext: function($yieldSent, $yieldAction) {
      while (true) try {
        return this.innerFunction($yieldSent, $yieldAction);
      } catch ($caughtException) {
        $storedException = $caughtException;
        switch ($state) {
          default:
            this.GState = 3;
            $state = -2;
            throw $storedException;
        }
      }
    }
  };
  return $__generatorWrap($G);
}
for (var $__0 = $traceurRuntime.getIterator(cool()), $__1; !($__1 = $__0.next()).done;) {
  n = $__1.value;
  {
    console.log(n);
  }
}
Answer:3

I have the following event-handling function: jQuery(document).on('click', '#button .submitb', function(e){alert();}); jQuery IS included in the html document. But, if I click on a <div id="...

I have the following event-handling function: jQuery(document).on('click', '#button .submitb', function(e){alert();}); jQuery IS included in the html document. But, if I click on a <div id="...

  1. jquery target selector

This may be a dumb question , not a completely programming related question but bear with me as I am trying to learn. How does FEEDLY retrive the feeds / news from a website just by entering the ...

This may be a dumb question , not a completely programming related question but bear with me as I am trying to learn. How does FEEDLY retrive the feeds / news from a website just by entering the ...

  1. what does service look like
  2. does honda have a service like onstar
  3. does jeep have a service like onstar

I am using a datepicker which came deafult with a html template , everything worked fine untill i used a custom jquery to hide a div using a radio button ... here are my top scripts <script type='...

I am using a datepicker which came deafult with a html template , everything worked fine untill i used a custom jquery to hide a div using a radio button ... here are my top scripts <script type='...

  1. jquery breaks another javascript

I have been working with the excellent noUISlider jQuery plugin and now I want to start using the npm version as I am moving my JS to use node and browserify. However I don't understand how to adapt ...

I have been working with the excellent noUISlider jQuery plugin and now I want to start using the npm version as I am moving my JS to use node and browserify. However I don't understand how to adapt ...

  1. browser version using jquery
  2. using multiple version of jquery
  3. check browser version using jquery