Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing IEnumerable in JavaScript / TypeScript

I'm trying to implement the C# keyword yield with in JavaScript/TypeScript (doesn't matter which): For example, I would like to implement the code:

//using System.Collections;  
//using System.Diagnostics; 
public static void Process()
{
    // Display powers of 2 up to the exponent of 8:  
    foreach (int number in Power(2, 8))
    {
        Debug.Write(number.ToString() + " ");
    }
    // Output: 2 4 8 16 32 64 128 256
}


public static IEnumerable Power(int baseNumber, int highExponent)
{
    int result = 1;

    for (int counter = 1; counter <= highExponent; counter++)
    {
        result = result * baseNumber;
        yield return result;
    }
}

in JavaScript.

The end goal is to implement a function written in C# from another question I asked about on stackoverflow, in JavaScript:

public static IEnumerable<string> SplitByCharacterType(string input)
{
    if (String.IsNullOrEmpty(input))
        throw new ArgumentNullException(nameof(input));

    StringBuilder segment = new StringBuilder();
    segment.Append(input[0]);
    var current = Char.GetUnicodeCategory(input[0]);

    for (int i = 1; i < input.Length; i++)
    {
        var next = Char.GetUnicodeCategory(input[i]);
        if (next == current)
        {
            segment.Append(input[i]);
        }
        else
        {
            yield return segment.ToString();
            segment.Clear();
            segment.Append(input[i]);
            current = next;
        }
    }
    yield return segment.ToString();
}

Any ideas?

like image 299
thed0ctor Avatar asked Dec 01 '12 21:12

thed0ctor


2 Answers

I don't think there's a reasonable way to make this work in the context of a for loop that preserves the C# semantics of lazy evaluation during the "move next" operation. You can simulate this reasonably with closures, though.

(TypeScript code):

function getPowers(base: number, maxExponent: number) {
    var currentExponent = 1;
    return function() {
        if(currentExponent > maxExponent) {
            return undefined;
        } else {
            return Math.pow(base, currentExponent++);
        }
    }
}

// Simple test
var p = getPowers(2, 8);
var n: number;
while((n = p()) !== undefined) {
    console.log(n);
}

// Demonstrate that multiple instances work
var p2 = getPowers(2, 3);
var p3 = getPowers(3, 3);
while(true) {
    var n2 = p2();
    var n3 = p3();
    if((n2 || n3) === undefined) break;

    console.log(n2 + ", " + n3);
}
like image 165
Ryan Cavanaugh Avatar answered Oct 20 '22 17:10

Ryan Cavanaugh


I know it's many years later, but generators & iterators now exist in TypeScript, so this is now possible. You can read more of the details here: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#typescript-23

I'm the original author of the SplitByCharacterType, so I figured I'd also give it a stab to try and re-implement in JS. The hardest problem is the lack of a native ability to differentiate character type (ex: GetUnicodeCategory). unicode-categories looks like it could be used to identify the character category. There is also an answer here for this. I'm going to use the second option as it looks more comprehensive. Note, that the getType() method used here is coming from that answer.

function* splitByCharacterType(input) {
    if (!input || !input.length) 
        return;

    var segment = [input[0]];
    var current = getType(input[0]);

    for (var i = 1; i < input.length; i++) {
        var item = input[i];
        var next = getType(item);
        if (next == current) {
            segment.push(item);
        } else {
            yield segment.join("");
            segment = [item];
            current = next;
        }
    }
    yield segment.join("");
}

This was also an interesting page on the subject: http://inimino.org/~inimino/blog/javascript_cset

like image 26
caesay Avatar answered Oct 20 '22 19:10

caesay