npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hodash.sliding-window

v0.1.0

Published

A utility library for generating sliding window iterations, perfect for bottom-up dynamic programming and other sequential processing patterns.

Downloads

82

Readme

@hodash/sliding-window

A utility library for generating sliding window iterations, perfect for bottom-up dynamic programming and other sequential processing patterns.

Installation

npm install @hodash/sliding-window

Overview

The sliding window utility helps you process sequences of data where you need to:

  • Build solutions based on previously computed results (memoization)
  • Implement bottom-up dynamic programming algorithms
  • Break any problem down that relies on solutions spanning every possible span of ranges within a list

Basic Usage

const config = {
  // Here is where we define the minimum and maximum span sizes.
  // A minimum of 1 means the spans will go all the way down to dealing
  // with neighboring indices (e.g. (0,1), (1,2), (2,3), (3,4), etc)
  // A maximum of 4 2 means the spans will go up to spanning 3 indices
  // (e.g. (0,3), (1,4), (2,5), (3,6), etc)
  size: { min: 1, max: 3 },
  // Here is where we define the range of indices we'd like to generate
  // spans within. If we limit it from index 0 to index 3, with the min
  // and max specified above, the full span of ranges we can expect to
  // uncover would be
  // For size 1:
  // (0,1) (1,2) (2,3)
  // For size 2:
  // (0,2) (1,3)
  // For size 3:
  // (0,3)
  index: { from: 0, to: 3 },
};

// If all we want to do is generate pairs, we have a generator
// available to us to do exactly that:
import { generatePairs } from '@hodash/sliding-window';

for (const { iteration, pair } of generatePairs(config)) {
  console.log(iteration, pair);
}

// If we want to generate pairs, and call a handler function as they
// come in, in which the returned value is memoized, and previous results
// are memoized and passed into the function, we can do that as well
import { slidingWindow } from '@hodash/sliding-window';

// Note, this uses the same config object as generatePairs
slidingWindow((payload, memo) => {
  // memo object is passed in the memoized results of previously run calls
  // of this handler function. So if we're dealing with the sequence above,
  // if I was on pair (1,3), for example, that means I would be able to access
  // memo[0][1]
  // memo[1][2]
  // memo[2][3]
  // memo[0][2]
  // As having the results of their handler stored

  // This will be stored at memo[1][3]
  return doSomething();
}, config);

Dynamic Programming Examples

1. Longest Palindrome substring

function longestPalindromicSubstring(s: string) {
  // Configuration for the sliding window
  const config = {
    index: {
      to: s.length - 1,
    },
  };

  type Tracker = {
    longest: string | null;
    current: string | null;
  };

  return slidingWindow<Tracker>((
    payload,
    memo,
    { child },
  ) => {
    const [i, j] = payload.pair;

    const tracker: Tracker = { longest: null, current: null };

    const areBookendsEqual = s[i] === s[j];

    // For substrings of length 1 and 2, there will be no substrings to examine. Thus
    // we set up the foundational cases by just checking if the bookending characters are equal
    if (payload.iteration < 2) {
      if (areBookendsEqual) tracker.current = tracker.longest = s.slice(i, j + 1);
      return tracker;
    }
    
    // If the bookends are equal, and the substring they enclose also have a current palindrome,
    // then we still have a palindrome.
    if (areBookendsEqual && child.middle?.current) {
      tracker.longest = tracker.current = s.slice(i, j + 1);
    }
    // Otherwise, we're not dealing with a palindrome, and are instead looking for the longest
    // palindrome to bubble up.
    else {
      if (child.left!.longest && child.left!.longest.length > (tracker.longest || '').length) {
        tracker.longest = child.left!.longest;
      }
  
      if (child.right!.longest && child.right!.longest.length > (tracker.longest || '').length) {
        tracker.longest = child.right!.longest;
      }
    }

    return tracker;
  }, config);
}

// The string to analyze for palindromic substrings
const result1 = longestPalindromicSubstring('zytxxty');
expect(result1[0][6].longest).to.equal('ytxxty');
expect(result1[0][6].current).to.equal(null);
expect(result1[1][6].longest).to.equal('ytxxty');
expect(result1[1][6].current).to.equal('ytxxty');

const result2 = longestPalindromicSubstring('zytxty');
expect(result2[0][5].longest).to.equal('ytxty');
expect(result2[0][5].current).to.equal(null);
expect(result2[1][5].longest).to.equal('ytxty');
expect(result2[1][5].current).to.equal('ytxty');

const result3 = longestPalindromicSubstring('aab');
expect(result3[0][2].longest).to.equal('aa');
expect(result3[0][2].current).to.equal(null);
expect(result3[0][1].longest).to.equal('aa');

API Reference

slidingWindow<T>(handler, config, bonus)

Parameters

  • handler: (payload: SlidingWindowPayload, memo: T[][], bonus: SlidingWindowBonus<T>) => T

    • Called for each window position
    • Receives current position details and memoization array
    • Should return value to be memoized
  • config: SlidingWindowConfig

    type SlidingWindowConfig = {
      size?: {
        min?: number;  // Minimum window size (default: 1)
        max?: number;  // Maximum window size (default: range)
      };
      index: {
        from?: number;  // Starting index (default: 0)
        to: number;     // Ending index (required)
      };
    }

Payload Structure

type SlidingWindowPayload = {
  pair: [number, number];    // Current indices being processed
  iteration: number;         // Current iteration number
  currentSize: number;       // Current window size
  step: number;             // Step size for current window
  direction: number;        // 1 for forward, -1 for reverse
  config: {                 // Configuration used
    size: { min: number; max: number };
    index: { from: number; to: number };
  };
  // Shorthand convenience for commonly accessed indices of previously-visited
  // pairs of indices.
  // [a, b, c, d, e]
  // If we are currently on the final range spanning (a, e), the children would be:
  // left: (0, 3) i.e. (a, d)
  // middle: (1, 3) i.e (b, d)
  // right: (1, 4) i.e. (b, e)
  child: {
    left: [number, number] | null;
    middle: [number, number] | null;
    right: [number, number] | null;
  };
}

Generator Usage

For more control over the iteration process, you can use the generatePairs generator directly:

import { generatePairs } from '@hodash/sliding-window';

for (const payload of generatePairs({
  index: { to: 5 },
  size: { min: 2, max: 3 }
})) {
  console.log(payload.pair); // Process pairs as needed
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.