itiriri

repository·master·Indexed 41 days ago

https://github.com/labs42io/itiriri

A library optimized for the ES6 iteration protocol providing a chainable API for manipulating iterables such as arrays, Maps, Sets, and generators. It features deferred execution (lazy evaluation) for memory efficiency and support for infinite iterables, offering methods like map, filter, join, and groupBy.

Tokens
17.6K
Snippets
74
Records
83
Agent score
47%

What's inside itiriri

  1. How deferred execution works in itiriri

    master

    Unlike standard JavaScript array methods that create shallow copies and execute immediately, itiriri functions that return iterables use deferred execution.

    Key behaviors:

    1. Lazy Evaluation: The source iterable is not processed until you chain a function that reduces the value (like .sum()) or transform it into a built-in type (like iterating with for...of).
    2. Single Pass: The source iterable is iterated only once.
    3. Memory Efficiency: Methods like filter, map, and concat do not buffer elements; they pipe them one-by-one through the chain.
    4. Infinite Iterables: Because of deferred execution, you can use itiriri with infinite generators (e.g., a Fibonacci sequence) as long as you use a terminal method like .take(n) to limit the results.

    Note: Functions that require the entire sequence to be known (like sort, reverse, or shuffle) expect a finite iterable.

    import itiriri from 'itiriri';
    
    function* fibonacci() {
      let [a, b] = [0, 1];
      while (true) {
        yield a;
        [a, b] = [b, a + b];
      }
    }
    
    // Finding first 3 Fibonacci numbers that contain '42'
    // This works because .take(3) stops the iteration
    const result = itiriri(fibonacci())
      .filter(x => x.toString().indexOf('42') !== -1)
      .take(3);
    
    for (const e of result) {
      console.log(e);
    }
    // outputs: 514229, 267914296, 7778742049
  2. Bundle itiriri for browser use

    master

    To use itiriri in a browser environment, you can create a minified version using gulp:

    1. Install dependencies: npm install
    2. Run the bundle task: npm install && gulp bundle
    3. This creates itiriri.min.js in your root folder.

    Include it in your HTML via a <script> tag:

    <script src="itiriri.min.js"></script>
    <script>
        const source = [1, 2, 3];
        console.log(itiriri(source).sum());
    </script>
  3. The IterableQuery interface

    master

    The IterableQuery<T> interface is the primary consumer-facing abstraction in itiriri. It extends the standard Iterable<T> and provides a fluent, chainable API for performing complex operations on sequences. Because it inherits from multiple specialized interfaces (like IterableFilter, IterableTransformation, and IterableJoin), an IterableQuery object allows you to mix and match operations like filtering, mapping, joining, and set operations in a single pipeline.

    Key characteristics:

    • Chainability: Most methods return a new IterableQuery<T>, allowing for functional-style pipelines.
    • Lazy/Deferred Execution: While the interface defines the operations, the actual computation is typically deferred until the sequence is consumed.
    • Rich API: It combines standard iterator methods (keys, values, entries) with advanced sequence manipulation (concat, prepend, fill).
  4. How IterableQuery works and its lifecycle

    master

    An IterableQuery<T> is a wrapper around an Iterable<T>. Most methods on the Itiriri class (like map, filter, take) are lazy: they return a new IterableQuery representing the transformation without immediately executing it. The actual iteration and computation occur only when you call a terminal method (a reducer or a cast) or manually iterate over the object using for...of or the [Symbol.iterator]() method.

    Key distinction:

    • Intermediate operations (e.g., map, filter, slice, skip) return a new IterableQuery and are lazy.
    • Terminal operations (e.g., toArray, sum, first, forEach, reduce) execute the pipeline and return a concrete value or perform an action.
    // Lazy pipeline construction
    const query = itiriri([1, 2, 3, 4, 5])
      .filter(x => x % 2 === 0)
      .map(x => x * 10);
    
    // Terminal operation triggers execution
    const result = query.toArray(); // [20, 40]
  5. Basic usage with arrays and iterables

    master

    itiriri can wrap arrays, Maps, Sets, generator functions, or any custom iterable. It provides methods similar to JavaScript arrays (like map, filter, slice, reduce, every, some) but optimized for ES6 iterators and designed for chaining.

    import itiriri from 'itiriri';
    
    const values = [2, 0, 4, 8];
    
    const s = itiriri(values).map(n => n / 2).reverse();
    console.log(s.toString()); // prints: 4,2,0,1
    
    // Iterating with for...of
    for (const n of s) {
      console.log(n)
    }
    
    // Using reduction methods
    console.log(s.sum()); // prints: 7
  6. Group elements with `groupBy()`

    master

    The groupBy() method groups elements by a key provided by a keySelector. You can optionally provide a valueSelector to transform the elements within each group.

    It returns a sequence of pairs: [key, IterableQuery<transformedValue>].

    Note: groupBy is a deferred method; it is only executed when the resulting sequence is iterated.

    import itiriri from 'itiriri';
    
    const students = [
      {name: 'Alice', gender: 'female'},
      {name: 'Bob', gender: 'male'},
      {name: 'David', gender: 'male'},
    ];
    
    itiriri(students).groupBy(elem => elem.gender, elem => elem.name).toArray();
    // [['female', ['Alice']], ['male', ['Bob', 'David']]]
  7. Get element at index with `nth`

    master

    Returns the element at a specified zero-based index.

    • For negative indices, it returns the element from the end of the sequence.
    • If the index is out of range, it returns undefined.

    Syntax:

    • nth(index: number): T
    import itiriri from 'itiriri';
    
    itiriri(['a', 'b', 'c', 'd']).nth(2)  // returns 'c'
    itiriri(['a', 'b', 'c', 'd']).nth(-1) // returns 'd'
    itiriri(['a', 'b', 'c', 'd']).nth(10) // returns undefined
  8. Find elements with `find()`, `findIndex()`, `findLast()`, and `findLastIndex()`

    master

    These methods allow you to locate elements or their indices based on a predicate:

    • find(predicate): Returns the first element that satisfies the predicate, or undefined if none found.
    • findIndex(predicate): Returns the index of the first element that satisfies the predicate, or -1 if none found.
    • findLast(predicate): Returns the last element that satisfies the predicate, or undefined if none found.
    • findLastIndex(predicate): Returns the index of the last element that satisfies the predicate, or -1 if none found.
    import itiriri from 'itiriri';
    
    itiriri([1, 2, 3, 4, 5]).find(elem => elem % 2 === 0); // returns 2
    itiriri([1, 2, 3]).findIndex(elem => elem > 10); // returns -1
    itiriri([11, 7, 21]).findLast(elem => elem > 10); // returns 21
    itiriri([11, 7, 21]).findLastIndex(elem => elem > 10); // returns 2
  9. Extract a range with `slice`

    master

    The slice method returns a sequence representing a range of elements from a start index to an end index (exclusive).

    Note: slice is a deferred method and is only executed upon iteration.

    import itiriri from 'itiriri';
    
    itiriri([1, 2, 3, 4, 5]).slice(1, 3).toArray(); // returns [2, 3]
  10. Perform a group join with `groupJoin`

    master

    The groupJoin method performs a join where each element from the source sequence is matched with an array of all elements from the joined sequence that share the same key. If no matches are found, the joinSelector is called with an empty array.

    Note: groupJoin is a deferred method; it is only executed when the resulting sequence is iterated (e.g., by calling .toArray()).

    import itiriri from 'itiriri';
    
    const books = [
      {title: 'Clean code', categoryId: 1 },
      {title: 'Code complete', categoryId: 1},
      {title: 'Scrum', categoryId: 2},
    ];
    
    const categories = [
      {id: 1, name: 'CS'},
      {id: 2, name: 'Agile'},
    ];
    
    itiriri(categories).groupJoin(
      books,
      category => category.id,
      book => book.categoryId,
      (category, books) => ({ category: category.name, books: books.map(b => b.title) })
    ).toArray();
    // [ 
    //   {category: 'CS', books: ['Clean code', 'Code complete']},
    //   {category: 'Agile', books: ['Scrum']}
    // ]