Skip to content

A Map polyfill until it's native #1064

@WebReflection

Description

@WebReflection

Is your feature request related to a problem? Please describe

For interoperability reasons, I'd like to have Map available (Set coming after if this works well).

Describe the solution you'd like

Imagine this map.js file is all we need to have import Map from './compat/map.js'; around, followed by a globalThis.Map ??= Map; after.

const iterator = Symbol.iterator;
const setPrototypeOf = Object.setPrototypeOf;

let Map = globalThis.Map;

if (!Map) {
  Map = function Map(iterable) {
    const keys = [];
    const values = [];
    const map = setPrototypeOf(
      {
        clear() {
          keys.length = 0;
          values.length = 0;
        },
        delete(key) {
          const i = keys.indexOf(key);
          if (i < 0) return false;
          keys.splice(i, 1);
          values.splice(i, 1);
          return true;
        },
        entries() {
          return map[iterator]();
        },
        forEach(callback) {
          for (let i = 0; i < keys.length; i++)
            callback(values[i], keys[i], map);
        },
        get(key) {
          const i = keys.indexOf(key);
          return i < 0 ? undefined : values[i];
        },
        getOrInsert(key, defaultValue) {
          const i = keys.indexOf(key);
          return -1 < i ? values[i] : (values[keys.push(key) - 1] = defaultValue);
        },
        getOrInsertComputed(key, callback) {
          const i = keys.indexOf(key);
          return -1 < i ? values[i] : (values[keys.push(key) - 1] = callback(key));
        },
        has(key) {
          return -1 < keys.indexOf(key);
        },
        keys() {
          return keys[iterator]();
        },
        set(key, value) {
          let i = keys.indexOf(key);
          if (i < 0) i = keys.push(key) - 1;
          values[i] = value;
          return map;
        },
        values() {
          return values[iterator]();
        },
        get size() {
          return keys.length;
        },
        [iterator]() {
          const iterable = [];
          for (let i = 0; i < keys.length; i++)
            iterable.push([keys[i], values[i]]);
          return iterable[iterator]();
        },
      },
      Map.prototype,
    );
    if (iterable) {
      const g = iterable[iterator]();
      do {
        const c = g.next();
        if (c.done) break;
        const entry = c.value;
        map.set(entry[0], entry[1]);
      } while (true);
    }
    return map;
  };
  
  Map.groupBy = function(iterable, callback) {
    const map = new Map;
    const values = [];
    const g = iterable[iterator]();
    do {
      const c = g.next();
      if (c.done) break;
      const value = c.value;
      const key = callback(value);
      map.getOrInsert(key, values).push(value);
    } while (true);
    return map;
  };
}

export default Map;

Describe alternatives you've considered

I haven't looked into the core of this project but:

  • I love it already!
  • I've been writing Web polyfills for 25+ years at this point
  • I'd like to help this move forward with Web compat as I can (and when I can, of course)

Additional context

Common polyfills will not work in here because there are too many constraints around what is still accepted in common JS and what is just not possible in here.

My polyfill experience is all about that, meaning I can write ad-hoc code working around common/modern APIs constraints, so here my first contribution because without Map I believe this is a no-go for tons of possibly working solutions out of the biggest PL ecosystem out there, and this is a Web related project too 🥰

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions