Skip to content

seriesoftubes/pycollections.js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pycollections.js

collections.py for JavaScript - DEPRECATED!

pycollections.js contains class definitions for Dict, DefaultDict, Counter, OrderedDict, and NamedTuple, stocked with functionality that (mostly) mirrors their collections.py counterparts and exceeds that of the ES6 Map class.

It has been tested on NodeJS v20.8.1, and the latest Chrome, Firefox, and Safari browsers.

This module is deprecated and unmaintained. It may have critical security vulnerabilities!

Please use TypeScript built-ins instead.

TypeScript built-ins have better test coverage, and they use the TS compiler.

Python dict in TypeScript

The TypeScript Map class is very close to Python's dict class.

collections.defaultdict in TypeScript

This TS code turns a native Map into a defaultdict:

class DefaultDict<K, V> {
    constructor(
        private readonly map: Map<K, V>,
        private readonly defaultFn: (key: K) => V
    ) {}

    get(key: K): V {
        if (this.map.has(key)) {
            return this.map.get(key)!;
        }
        const val = this.defaultFn(key);
        this.map.set(key, val);
        return val;
    }
}

collections.Counter in TypeScript

A collections.Counter is basically a DefaultDict with a default function that returns 0, plus some extras for getting the most common elements. The pycollections.js library never did anything fancy like the heap queue algorithm anyway.

collections.OrderedDict in TypeScript

The built-in Map preserves the insertion order of its entries.

collections.namedtuple in TypeScript

TypeScript's type aliases or interfaces are good substitutes for namedtuples.


Contents


Getting it

NPM

npm install pycollections

Bower

bower install pycollections

cdnjs

Coming soon!



Dict

Static methods:

  • fromKeys(keys, opt_valueForAllKeys)

Instance methods:

  • clear()
  • copy()
  • set(key, value)
  • update(iterable)
  • hasKey(key)
  • get(key, opt_defaultValue)
  • del(key)
  • pop(key, opt_defaultValue)
  • iterkeys(cb)
  • keys()
  • popitem()
  • length()
  • iteritems(cb)
  • items()
  • itervalues(cb)
  • values()

Instance methods (not present in Python dict):

  • getFirstKey()
  • getFirstMatchingKey(predicate)
  • isEmpty()
  • setOneNewValue(key, fn)
  • setSomeNewValues(keys, fn)
  • setAllNewValues(fn)

Demo

Creating a new Dict

py = {}
py = dict.fromkeys([1, 2, 3], {'the': 'value'})
py = dict(a=1, b=2)
py = dict([('a', 1), ('b', 2)])
py = dict(dict(a=1))
py = dict(123)  # raises TypeError
var js = new Dict();
var js = Dict.fromKeys([1, 2, 3], {'the': 'value'});
var js = new Dict({'a': 1, 'b': 2});
var js = new Dict([['a', 1], ['b', 2]]);
var js = new Dict(new Dict({'a': 1}));
var js = new Dict(123);  // throws Error

Detecting presence of a key

py = {}
987 in py  # False
[] in py  # raises TypeError due to unhashable key
var js = new Dict();
js.hasKey(987);  // false
js.hasKey([]);  // throws DictKeyNotHashable

Getting a potentially-missing key's value

py = {}
py['missing key']  # raises KeyError
py.get('missing key', 'default')  # 'default'
py.get('missing key')  # None
py[[]]  # raises TypeError due to unhashable key
py.get([])  # raises TypeError due to unhashable key
var js = new Dict();
js.get('my key');  // throws DictKeyNotFound
js.get('missing key', 'default');  // 'default'
// Must explicitly pass null/undefined as the default;
// when passing only one .get() argument, the key is must exist,
// otherwise it thows an error.
js.get('missing key', null);  // null
js.get([]);  // throws DictKeyNotHashable
js.get([], null);  // throws DictKeyNotHashable

Distinguishing between keys of different types

Support for distinct Boolean, Number, String, NaN, null, and undefined keys.

py = {}
py[8] = 'num'
8 in py  # True
'8' in py  # False
len(py)  # 1

py['8'] = 'str'
len(py)  # 2
py[8]  # 'num'
py['8']  # 'str'
var js = new Dict();
js.set(8, 'num');
js.hasKey(8); // true
js.hasKey('8'); // false
js.length(); // 1

js.set('8', 'str');
js.length();  // 2
js.get(8);  // 'num'
js.get('8');  // 'str'

Deleting a key

py = {'a': 1}
del py['a']
del py['b']  # raises KeyError
var js = new Dict({'a': 1});
js.del('a');
js.del('b');  // throws DictKeyNotFound

Popping a key

py = {'a': 1, 'b': 2}
py.pop('a')  # 1
py.popitem()  # ('b', 2)
py.pop(123)  # raises KeyError
py.pop(123, 45)  # 45
var js = new Dict({'a': 1, 'b': 2});
js.pop('a');  // 1
js.popitem(); // ['b', 2]
js.pop(123);  // throws DictKeyNotFound
js.pop(123, 45);  // 45

Clearing out all key-value pairs

py = {'a': 1, 'b': 2}
len(py)  # 2
py.clear()
len(py)  # 0
py['a']  # raises KeyError
var js = new Dict({'a': 1, 'b': 2});
js.length();  // 2
js.clear();
js.length();  // 0
js.get('a');  // throws DictKeyNotFound

Forming a copy

py1 = {'a': 1, 'b': 2}
py2  = py1.copy()
py1['a'] = 987
py2['a']  # 1
var js1 = new Dict({'a': 1, 'b': 2});
var js2 = js1.copy();
js1.set('a', 987);
js2.get('a');  // 1

Updating key-value pairs

py = {'a': 1, 'b': 2}
py.update({'a': 999})
py['a']  # 999
len(py)  # 2
py.update([('b', 123)])
py['b']  # 123
var js = new Dict({'a': 1, 'b': 2});
js.update({'a': 999});
js.get('a');  // 999
js.length();  // 2
js.update([['b', 123]]);
js.get('b');  // 123

Iterating over keys, values, and items

py = {'a': 1}
# does not put all keys into memory at once
for key in py:
  print key  # prints 'a'
py.keys()  # ['a']

# does not put all values into memory at once
for value in py.itervalues():
  print value  # prints 1
py.values()  # [1]

# does not put all items into memory at once
for item in py.iteritems():
  print item  # prints ('a', 1)
py.items()  # [('a', 1)]
var js = new Dict({'a': 1});
// does not put all keys into memory at once
js.iterkeys(function(key) {
  console.log(key);  // logs 'a'
});
js.keys();  // ['a']

// does not put all values into memory at once
js.itervalues(function(value) {
  console.log(value);  // logs 1
});
js.values();  // [1]

// does not put all items into memory at once
js.iteritems(function(key, value) {
  console.log(key, value);  // logs 'a', 1
});
js.items();  // [['a', 1]]

Using non-Python methods

var js = new Dict();
js.isEmpty();  // true
js.set('key', 99);
js.isEmpty();  // false
// executes without putting all keys into a new array in memory
js.getFirstKey();  // 1
// executes without putting all keys into a new array in memory
js.getFirstMatchingKey(function(k) {
  return typeof(k) === 'number';
 });  // throws DictKeyNotFound
js.getFirstMatchingKey(function(k) {
  return typeof(k) === 'string';
});  // 'key'

// Since there is no direct access to stored elements,
// you can't do things like "py[123] += 1".
// This is especially true of keys whose values are primitive types:
// Boolean, String, or Number.
// Instead, you must do one of the following:
js.setOneNewValue('key', function(currentValue) {
  return currentValue + 10;
});
js.get('key');  // 109

js.setSomeNewValues(['key'], function(currentValue) {
  return currentValue + 10;
});
js.get('key');  // 119

js.setAllNewValues(function(currentValue) {
  return currentValue + 10;
});
js.get('key');  // 129

js.set('array key', []);
js.get('array key').push(123);
js.get('array key');  // [123]


DefaultDict

Static methods: same as Dict (DefaultDict is a subclass of Dict)

Instance methods: same as Dict

Demo

Creating a new DefaultDict

py = defaultdict(list)
py = defaultdict(int, a=1)
py = defaultdict(str, [('a', 1)])
py = defaultdict(int, dict(a=1))
py = defaultdict(123)  # raises TypeError
var js = new DefaultDict([].constructor);
var js = new DefaultDict(function(){return 0}, {'a': 1});
var js = new DefaultDict(String, [['a', 1]]);
var js = new DefaultDict(function(){return 0}, new Dict({'a': 1}));
var js = new DefaultDict(123);  // throws Error

Getting a potentially-missing key's value

py = defaultdict(int)
len(py)  # 0
py[1]  # 0
len(py)  # 1
py.keys()  # [1]
py.get(123)  # None
len(py)  # 1
py.keys()  # [1]
var js = new DefaultDict(function(){return 0});
js.length();  // 0
js.get(1);  // 0
js.length();  // 1
js.keys();  // [1]
js.get(123, null);  // null
js.length();  // 1
js.keys();  // [1]

Operating on potentially-missing keys' values

py = defaultdict(list)
for letter in 'abc':
  py[letter].append(123)
py.items()  # [('a', [123]), ('b', [123]), ('c', [123])]
var js = new DefaultDict([].constructor);
'abc'.split('').forEach(function(letter) {
  js.get(letter).push(123);
});
js.items();  // [['a', [123]], ['b', [123]], ['c', [123]]]

DefaultDict of Dicts

py = defaultdict(dict)
py[123][0] = 9
nested_dict = py[123]
nested_dict[0]  # 9
var js = new DefaultDict(function(){return new Dict()});
js.get(123).set(0, 9);
var nestedDict = js.get(123);
nestedDict.get(0);  // 9

Fun with self-referencing

py = defaultdict(lambda: py)
py[1][1][2][3] = 123
len(py)  # 3
py[1] is py  # True
3 in py  # True
2 in py  # True
1 in py  # True
py[3]  # 123
var js = new DefaultDict(function(){return js});
js.get(1).get(1).get(2).set(3, 123);
js.length();  // 3
js.get(1) === js;  // true
js.hasKey(3);  // true
js.hasKey(2);  // true
js.hasKey(1);  // true
js.get(3);  // 123


Counter

Static methods: same as DefaultDict, with some changes

  • fromKeys() is not implemented (throws an error)
  • getIncrementor(incrementBy)

Instance methods: same as DefaultDict, plus some new ones

  • iterelements(cb)
  • elements()
  • subtract(iterable)
  • mostCommon(opt_n)
  • leastCommon(opt_n)

Demo

Creating a new Counter

py = Counter()
py = Counter(a=2, b=1)
py = Counter(['a', 'a', 'b'])
py = Counter(123)  # raises TypeError
var js = new Counter();
var js = new Counter({'a': 2, 'b': 1});
var js = new Counter(['a', 'a', 'b']);
var js = new Counter(123);  // throws Error

Counting distinct elements

py = Counter()
py.update(['a', 'a', 32, 'false', False, float('inf')])
py.items()  # [('a', 2), (32, 1), ('false', 1), (False, 1), (inf, 1)]
list(py.elements())  # ['a', 'a', 32, 'false', False, inf]
var js = new Counter();
js.update(['a', 'a', 32, 'false', false, NaN]);
js.items();  // [['a', 2], [32, 1], ['false', 1], [false, 1], [NaN, 1]]
js.elements();  // ['a', 'a', 32, 'false', false, NaN]

Increasing/decreasing counts

py = Counter(['a', 'b', 'b', 'c', 'c', 'c'])
py[99]  # 0
py['a']  # 1
py['b']  # 2
py['c']  # 3
py.update(['a', 'a', 'b'])
py['a']  # 3
py['b']  # 3
py[99]  # 0
py['c']  # 3

py.subtract({'a': 4})
py['a']  # -1
var js = new Counter(['a', 'b', 'b', 'c', 'c', 'c']);
js.get(99);  // 0
js.get('a');  // 1
js.get('b');  // 2
js.get('c');  // 3
js.update(['a', 'a', 'b'])
js.get('a');  // 3
js.get('b');  // 3
js.get(99);  // 0
js.get('c');  // 3

js.subtract({'a': 4})
js.get('a')  // -1

Getting the most common elements

py = Counter(['a', 'b', 'b', 'c', 'c', 'c'])
py.most_common(1)  # [('c', 3)]
py.most_common(2)  # [('c', 3), ('b', 2)]
py.most_common(99)  # [('c', 3), ('b', 2), ('a', 1)]
py.most_common()  # [('c', 3), ('b', 2), ('a', 1)]
var js = new Counter(['a', 'b', 'b', 'c', 'c', 'c']);
js.mostCommon(1);  // [['c', 3]]
js.mostCommon(2);  // [['c', 3], ['b', 2]]
js.mostCommon(99);  // [['c', 3], ['b', 2], ['a', 1]]
js.mostCommon();  // [['c', 3], ['b', 2], ['a', 1]]


OrderedDict

Static methods: same as Dict (it's a Dict subclass)

Instance methods: same as Dict

Demo

Creating a new OrderedDict

py = OrderedDict()
py = OrderedDict(a=1, b=2)
py = OrderedDict([('a', 1), ('b', 2)])
var js = new OrderedDict();
var js = new OrderedDict({a:1, b:2});
var js = new OrderedDict([['a', 1], ['b', 2]]);

Maintaining the order of keys and values

py = OrderedDict()
py['1st'] = 0
py['2nd'] = 1
py['3rd'] = 2
py.keys()  # ['1st', '2nd', '3rd']
py.values()  # [0, 1, 2]

py.pop('1st')  # 0
py.keys()  # ['2nd', '3rd']
py.values()  # [1, 2]

py['1st'] = 0
py.keys()  # ['2nd', '3rd', '1st']
py.values()  # [1, 2, 0]
var js = new OrderedDict();
js.set('1st', 0);
js.set('2nd', 1);
js.set('3rd', 2);
js.keys();  // ['1st', '2nd', '3rd']
js.values();  // [0, 1, 2]

js.pop('1st');  // 0
js.keys();  // ['2nd', '3rd']
js.values();  // [1, 2]

js.set('1st', 0);
js.keys();  // ['2nd', '3rd', '1st']
js.values();  // [1, 2, 0]


NamedTuple

Demo

Defining a new NamedTuple class

Py = namedtuple('Py', ['a', 'b'])
var Js = NamedTuple('Js', ['a', 'b']);

Creating instances of the new class

py = Py(1, 2)  #  Py(a=1, b=2)
py = Py(1)  # throws TypeError
py = Py(1, 2, 3)  # throws TypeError
var js = new Js(1, 2);
js.toString();  // "Js(a=1, b=2)"
var js = new Js(1);  // throws Error
var js = new Js(1, 2, 3);  // throws Error

Accessing an instance's properties directly

py = Py(1, [2, 3])
py.a  # 1
py.b  # [2, 3]
py[0]  # 1
py[1]  # [2, 3]

py.a = 123  # raises AttributeError
py[1] = 99  # raises TypeError
var js = new Js(1, [2, 3]);
js.a;  // 1
js.b;  // [2, 3]
js[0];  // 1
js[1];  // [2, 3]

js.a = 99;  // throws Error in strict mode, otherwise changes nothing
js[1] = 99;  // changes nothing

Accessing an instance's properties via Array functions

py = Py(1, 2)
for value in py:
  print value  # prints 1 then 2
all(py)  # True
var js = new Js(1, 2);
js.forEach(function(value, index) {
  console.log(value, index);  // logs [1, 0] then [2, 1]
});
js.every(function(v){return v > 0});  // true

About

collections.py for JavaScript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published