ClojureScript's persistent data structures and supporting API from the comfort of vanilla JavaScript

Related tags

Data Structure mori
Overview

mori

Mori

A simple bridge to ClojureScript's persistent data structures and supporting APIs for vanilla JavaScript. Pull requests welcome.

Breaking changes in 0.3.0

The API now uses idiomatic JavaScript naming conventions.

Improvements to 0.3.0

Faster

Mori is considerably faster across the board thanks to recent enhancements to the ClojureScript compiler. For users who would like to benchmark their immutable data structure implementations against Mori, Mori now exposes direct arity invokes which eliminates previous calling overheads from arity dispatching. See Benchmarking for more information.

Mori hash maps now default to ClojureScript ArrayMaps that are automatically promoted to PersistentHashMaps as needed. ArrayMaps deliver considerably better performance at small sizes and when simple keys are at play. For example a Mori hash map with less than or equal to eight keys can now be built nearly an order of magnitude faster than Immutable.js 3.6.2 Maps.

More ES6

All Mori collections support ES6 iteration via foo[Symbol.iterator] or foo["@@iterator"].

Differences from Immutable.js

  • A functional API, data structures do not have public methods
  • Faster, ClojureScript data structures have been subjected to more real world usage and continuous benchmarking for nearly 4 years
  • Larger, gzipped the base Mori module is about 6K larger than Immutable.js

Getting it

You can install the latest release via npm:

npm install mori

The installed package contains a single optimized JavaScript file mori.js.

Load mori in your Node.js programs as you would any other module:

var mori = require("mori");

In a browser, you can load mori with a script tag, as you would any other JavaScript library:

<script src="mori.js" type="text/javascript"></script>

You can also load it as an AMD module, e.g. with RequireJS.

Usage

You can use it from your projects like so:

var inc = function(n) {
  return n+1;
};

mori.intoArray(mori.map(inc, mori.vector(1,2,3,4,5)));
// => [2,3,4,5,6]

Efficient non-destructive updates!

var v1 = mori.vector(1,2,3);
var v2 = mori.conj(v1, 4);
v1.toString(); // => '[1 2 3]'
v2.toString(); // => '[1 2 3 4]'
var sum = function(a, b) {
  return a + b;
};
mori.reduce(sum, mori.vector(1, 2, 3, 4)); // => 10

Lazy sequences!

var _ = mori;
_.intoArray(_.interpose("foo", _.vector(1, 2, 3, 4)));
// => [1, "foo", 2, "foo", 3, "foo", 4]

Or if it's more your speed, use it from CoffeeScript!

inc = (x) -> x+1  
r = mori.map inc, mori.vector(1,2,3,4,5)
mori.intoArray r

Documentation

You can find extensive documentation and examples here.

More Examples

Efficient Freeze/Thaw

For vectors and maps we provide an efficient thaw and freeze operations:

var m = mori;

// ~220ms with V8 version 3.29.80 MBP 2.26ghz
for(var j = 0; j < 10; j++) {
  var s = new Date();
  var arr = [];
  for(var i = 0; i < 10000000; i++) {
    arr.push(i);
  }
  print("Array push " + arr.length + " items " + ((new Date())-s));
  gc();
}

// ~70ms
for(var j = 0; j < 10; j++) {
  s = new Date();
  var mv = m._thaw(m.vector());
  for(var i = 0; i < 10000000; i++) {
    mv = m._conj.f2(mv, i);
  }
  var v = m._freeze(mv);
  print("Mutable vector conj " + m.count(v) + " items " + ((new Date())-s));
  gc();
}

ES6 Map/Set inspired interfaces

All Mori maps and sets support all the non-mutating methods of the proposed ES6 Map and Set interfaces. The main difference with the spec is that key lookup is based on value not reference. keys, values, and entries methods return the proposed mutable iterators:

var m = mori;
var h = m.hashMap("foo", 1, "bar", 2);

h.has("foo"); // => true
h.get("foo"); // => 1

var iter = h.keys();
iter.next(); // => {done: false, value: "foo"}

This feature is subject to changes in the ES6 proposal.

Transducers

Mori includes Transducers. Zero allocation collection operations FTW:

var m = mori;
var a = [];

for(var i = 0; i < 1000000; i++) {
  a.push(i);
}

// make it immutable
var v = m.into(m.vector(), a);

function time(f) {
  var s = new Date();
  f();
  console.log(((new Date())-s)+"ms");
}

// ~190ms V8 version 3.29.80 MBP 2.26ghz
time(function() {
  var xf = m.comp(m.map(m.inc), m.map(m.inc), m.map(m.inc));
  return m.transduce(xf, m.completing(m.sum), 0, v);
}, 10);

// ~440ms
time(function() {
  return a.map(m.inc).map(m.inc).map(m.inc).reduce(function(a,b){return a+b;}, 0);
}, 10);

Build

Prerequisites

You will first need to install the Java SDK, if it's not already installed on your system.

On Windows, you will need to manually install Leiningen. On UNIX-like systems, Leiningen will be installed within the project automatically if the lein executable is not found on your path or if your lein version predates 2.0.0.

Clone the repo

git clone https://github.com/swannodette/mori.git
cd mori

On a UNIX-like system build with

./scripts/build.sh

Alternatively using npm

npm run-script build

On Windows

./scripts/build.ps1

The build process will generate an optimized JavaScript file mori.js, which is suitable for use with Node.js, or in a Web browser or other JavaScript environments. You can also load it as an AMD module.

Copyright (C) 2012-2015 David Nolen and contributors

Distributed under the Eclipse Public License, the same as Clojure.

Comments
  • function naming convention

    function naming convention

    This is a total bikeshedding issue (see Wadler's Law), so feel free to close this, but I was curious why you didn't use standard JavaScript function/method naming conventions with the mori API. I searched the open and closed issues and found nothing.

    For example, why mori.is_list instead of mori.isList? It follows neither a normal JavaScript naming convention (camelCase) nor the Clojure function naming convention (kebab-case) (because that syntax would not accessible via dot notation). Is this the result of a default in the clojurescript compiler?

    I can't help but imagine the gut reaction my colleagues would have to seeing snake_case appearing over any shared codebase I contribute to. Obviously this isn't going to stop me from experimenting and using mori, because it looks like an excellent library, but it's really funky to have all your code and all your libraries use the same naming conventions as ECMA-262, except for one.

    Given that mori.js is transpiled from clojurescript code to JavaScript, I reckon it should be possible to define which naming convention you want used in the generated code. Is this already possible with the clojurescript compiler? I looked at the sample options for lein-cljsbuild and didn't see anything there for defining the naming convention (https://github.com/emezeske/lein-cljsbuild/blob/1.0.3/sample.project.clj)

    opened by andrewdeandrade 11
  • How does Mori deal with functors, types, and interfaces in JS?

    How does Mori deal with functors, types, and interfaces in JS?

    When Mori builds a list, how does it define a functor interface saying that the list has a map? And more specifically, how is this transpiled into JS? does it use .prototype methods?

    opened by ccorcos 9
  • Keyword support

    Keyword support

    Added keyword and is_keyword. Added optional boolean argument to js_to_clj for converting string keys to keywords. Added test.

    Should close issue #48.

    opened by lantiga 7
  • Define shorthand for mori.curry(mori.get,

    Define shorthand for mori.curry(mori.get, "key")

    I've found myself writing things like this quite a lot:

    m = require('mori');
    var getName = m.curry(m.get, "name")
    
    var names = m.map(getName, collection_of_hash_maps);
    

    In real clojure, we'd use keywords for this case, but that's trickier in mori - is this something other people are running into as well?

    I'm not sure what a good name for this would be, some suggestions are below

    m.key("name")
    m.get_key("name")
    m.partial_key("name")
    m.curry_get("name")
    m.getter("name")
    
    opened by glenjamin 6
  • add symbols

    add symbols

    Hi there! I'm implementing a toy programming language in JS, on top of mori's data structures. The only thing I was really missing was a symbol type. I'm not sure if it's reasonable to include this in mori itself — I'll gladly maintain my fork for my use-case if you think it's not.

    opened by edef1c 6
  • Mori functions return errors when passing JavaScript Arrays

    Mori functions return errors when passing JavaScript Arrays

    CoffeeScript version 1.6.3 Node v0.10.24 mori 0.2.6.

    This is happening in the browser as well.

    One example is when passing an array to a mori function we return an ISeqable error.

    s = mori.set(["cat", "dog", "bird"])
    Error: cat,dog,birdis not ISeqable
        at Error (<anonymous>)
        at C (/Users/hcnewsom/node_modules/mori/mori.js:27:196)
        at Object.set (/Users/hcnewsom/node_modules/mori/mori.js:312:111)
        at repl:3:14
        at REPLServer.replDefaults.eval (/usr/local/lib/node_modules/coffee-script/lib/coffee-script/repl.js:33:28)
        at repl.js:239:12
        at Interface.<anonymous> (/usr/local/lib/node_modules/coffee-script/lib/coffee-script/repl.js:62:9)
        at Interface.EventEmitter.emit (events.js:117:20)
        at Interface._onLine (readline.js:202:10)
        at Interface._line (readline.js:531:8)
        at Interface._ttyWrite (readline.js:760:14)
        at ReadStream.onkeypress (readline.js:99:10)
        at ReadStream.EventEmitter.emit (events.js:117:20)
        at emitKey (readline.js:1095:12)
        at ReadStream.onData (readline.js:840:14)
        at ReadStream.EventEmitter.emit (events.js:95:17)
        at ReadStream.<anonymous> (_stream_readable.js:746:14)
        at ReadStream.EventEmitter.emit (events.js:92:17)
        at emitReadable_ (_stream_readable.js:408:10)
        at emitReadable (_stream_readable.js:404:5)
        at readableAddChunk (_stream_readable.js:165:9)
        at ReadStream.Readable.push (_stream_readable.js:127:10)
        at TTY.onread (net.js:526:21)
    

    In my web application I have a function that returns an object. When attempting to assoc_in to this object I have to explicitly wrap it in mori.hash_map in order to prevent an IAssociative error.

    opened by newsomc 6
  • hash collision for non equal collections

    hash collision for non equal collections

    I'm not sure if this is a bug or not. The following two vectors of vectors are producing equal hashes.

    console.log(m.hash(m.vector(m.vector(13, 5), m.vector(13, 4))))
    // => 803955717
    console.log(m.hash(m.vector(m.vector(13, 4), m.vector(12, 4))))
    // => 803955717
    

    They do not equal, just the hashes.

    I noticed the issue when using m.hash as a react component key (duplicate key errors). I put in an m.set to remove duplicates and still got the issue:

    segments = m.set(m.map(m.vector, this.state.path, m.rest(this.state.path)))
    console.log(m.clj_to_js(m.partition_by(m.hash, segments)))
    // produces partitions with count > 1
    

    I can produce several different combinations that duplicate hashes (e.g. [[7, 7], [7, 6]], [[7, 6], [6, 6]] // => 804048423).

    version of Mori is 0.2.5

    opened by eyston 6
  • Maximum call stack size exceeded while evaluating lazy sequence

    Maximum call stack size exceeded while evaluating lazy sequence

    This is probably an issue for the upstream ClojureScript project. But I wanted to post this to see if anyone knows of a workaround.

    I am working on a blog post on Mori with some examples of lazy evaluation. Here is what I have at the moment:

    // With no arguments, `range` returns a lazy sequence of all whole
    // numbers from zero up.
    var non_neg_ints = mori.range();
    
    // Dropping the first element, zero, results in a sequence of all of
    // the natural numbers.
    var nats = mori.drop(1, non_neg_ints);
    
    // Let's take just the powers of 2.
    var ln_2   = function(n) { return Math.log(n) / Math.LN2; };
    var pows_2 = mori.filter(function(n) { return ln_2(n) % 1 === 0; }, nats);
    
    // What are the first 10 powers of 2?
    console.log(
        mori.take(10, pows_2)
    );
    
    // Outputs:
    // > (1 2 4 8 16 32 64 128 256 512)
    

    That works nicely. But if I change the number of evaluated sequence elements from 10 to 15 (in the node repl) I get a stack overflow exception:

    > mori.take(15, pows_2);
    
    /home/jesse/tmp/node_modules/mori/mori.js:26
    b?this.Ca===b.Ca:l};Ub.prototype.toString=m("Ca");function D(a){if(a==j)return
                                                                        ^
    RangeError: Maximum call stack size exceeded
    
    opened by hallettj 6
  • Removing items positionally from a vector

    Removing items positionally from a vector

    First off, thanks for creating Mori. I am making use of it in a long-term project and it appears to be a good fit for my needs.

    I am wondering if it would be possible to port the JavaScript splice method into Mori. It would effectively allow removal and/or insert into sequences where item order is pertinent. Currently, conj seems limiting as it allows no flexibility as to where an item is added. Maybe subvec could play a part in this?

    When you consider for example that sometimes order is a significant thing in the real world, it makes sense to allow for the ordering of data even in persistent data structures. For example, a user creates a vector of todos, adding one after the other and then occasionally prioritizes them, moving the more important ones up the list.

    Currently, when I manipulate the order, it requires that I first call into_array, perform the reordering, and then reconstitute the vector. I am hoping that existing internal data structures can be reused in allowing for the same operation with greater efficiency.

    In another use case, it is possible that a todo is completed and thus removed from the list from somewhere in the middle. I realize there is a remove method that could be used but I wonder about efficiency. When your GUI is already aware of the index of a given item, it seems that this fact would allow for a more efficient operation. For example when you remove an item from a list of hundreds of items and you already know the index that must be better than having to filter it out via remove. It would also probably be useful if one could retrieve the position of an item in a sequence (i.e. indexOf) as this would go hand in hand with splice and even nth. At the very least, providing splice would provide a familiar and immensely useful api.

    Obviously, I could add these methods myself, but I think I'd end up with something that internally was as inefficient as the workarounds I'm already utilizing.

    opened by mlanza 6
  • Is mori still viable?

    Is mori still viable?

    In your opinion, is mori still a viable approach for providing persistent data structures to JavaScript?

    I tried building after cloning the library and updating project.clj to the latest cljsbuild, but the mori.node.js file that gets created is throwing exceptions related to goog.string.

    Rather than spending a bunch of time hunting that down, I thought it might be wise to first ask ask if you still think mori is viable and/or worthwhile, i.e perhaps ClojureScript developments in the last year+ have changed the landscape.

    Thank you.

    opened by michaelsbradleyjr 6
  • Mori.js should not be minified

    Mori.js should not be minified

    It is not a good practice to already come with a minified version for npm packages.

    This will slow down minification processes, i.E. gulp-uglify2 enormously.

    Ultimately, it is up to the developer to minify code in the build process of the app using Mori.

    That said, I recommend you to ship with an unminified mori.js and have another mori.min.js

    opened by binarykitchen 5
  • toClj throws exception for js object with property

    toClj throws exception for js object with property "j" having a numeric value

    This is a funny one.

    Using node 7.4.0 and mori 0.3.2:

    > var m = require("mori");
    undefined
    > m.toClj({j: 42});
    Error: No protocol method IEmptyableCollection.-empty defined for type object: [object Object]
        at x (/Users/jed/tmp/node_modules/mori/mori.js:17:63)
        at Na (/Users/jed/tmp/node_modules/mori/mori.js:19:96)
        at Oc (/Users/jed/tmp/node_modules/mori/mori.js:59:358)
        at v (/Users/jed/tmp/node_modules/mori/mori.js:385:474)
        at Function.b [as d] (/Users/jed/tmp/node_modules/mori/mori.js:387:109)
        at Function.a [as b] (/Users/jed/tmp/node_modules/mori/mori.js:385:42)
        at Object.b (/Users/jed/tmp/node_modules/mori/mori.js:422:95)
        at Object.c [as toClj] (/Users/jed/tmp/node_modules/mori/mori.js:422:168)
        at repl:1:3
        at realRunInThisContextScript (vm.js:22:35)
    > 
    

    Adding other properties does not change the behavior. Changing "j" to anything else seems to avoid the issue.

    opened by noonian 1
  • add vec as PersistentVector.fromArray

    add vec as PersistentVector.fromArray

    I was running some performance tests on mori vs. immutable, and found that we could make vector creation from a JS array at least two times faster:

    https://gist.github.com/edge/571bc3da31cb37d98addd78cc937f4d6

    113 ms M linear conj
    56 ms M atomic instance (apply)
    28 ms M atomic instance (vec)
    882 ms M js->clj
    522 ms I linear push
    238 ms I batched linear push
    83 ms I atomic instance
    215 ms I fromJS
    

    The idiomatic way to create a vector with 100 zeroes (yielding 113 ms):

    let v = vector()
    for (let i = 0; i < 100; i++) {
    	v = conj(v, 0)
    }
    

    A slightly more efficient way (yielding 56 ms):

    const v = vector.apply(null, Array(100).fill(0))
    

    However, this approach is limited to the number of arguments per call that the Javascript engine supports (though still quite a high number).

    The arguments object is actually quite slow in Javascript, and this is well documented.

    By skipping certain checks when we know that we are instantiating from an array, we can eschew the expensive conversion ([& args]), avoid the function call limitation, and provide a cleaner interface (yielding 28 ms):

    const v = vec(Array(100).fill(0))
    

    I also experimented with using cljs.core/vec, but found it even slower than the apply strategy, perhaps because PersistentVector.fromArray is that much more efficient.

    opened by alkhe 0
  • Unix like Build Failing on fresh repository.

    Unix like Build Failing on fresh repository.

    Running the build as described in the README.md on OSX doesn't work. It looks like the build script expects a file mori.bare.js to be at the root of the project but I can't for the life of me figure out how it's expected to be there.

    Logs:

    $ git clone https://github.com/swannodette/mori.git
    Cloning into 'mori'...
    remote: Counting objects: 1201, done.
    remote: Total 1201 (delta 0), reused 0 (delta 0), pack-reused 1200
    Receiving objects: 100% (1201/1201), 270.52 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (581/581), done.
    $ cd mori
    $ ./scripts/build.sh
    Retrieving org/clojure/clojurescript/0.0-3308/clojurescript-0.0-3308-aot.jar from central
    Compiling ClojureScript.
    Retrieving org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar from central
    Compiling "/Users/kasuko/Documents/Projects/mori/target/cljsbuild-main.js" from ["src"]...
    Analyzing jar:file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/cljs/core.cljs
    WARNING: sequence at line 1581 is being replaced at line 3196 file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/cljs/core.cljs
    WARNING: rand at line 2017 is being replaced at line 8547 file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/cljs/core.cljs
    WARNING: rand-int at line 2022 is being replaced at line 8553 file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/cljs/core.cljs
    Compiling src/mori/extra.cljs
    Analyzing jar:file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/clojure/zip.cljs
    Analyzing jar:file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/clojure/set.cljs
    Analyzing jar:file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/clojure/data.cljs
    Analyzing jar:file:/Users/kasuko/.m2/repository/org/clojure/clojurescript/0.0-2411/clojurescript-0.0-2411.jar!/cljs/reader.cljs
    Compiling src/mori.cljs
    WARNING: mori is a single segment namespace at line 1 src/mori.cljs
    Compiling src/mori/mutable.cljs
    Compiling release/clojure/zip.cljs
    Compiling release/clojure/set.cljs
    Compiling release/cljs/reader.cljs
    Compiling release/cljs/core.cljs
    WARNING: sequence at line 1581 is being replaced at line 3196 release/cljs/core.cljs
    WARNING: rand at line 2017 is being replaced at line 8547 release/cljs/core.cljs
    WARNING: rand-int at line 2022 is being replaced at line 8553 release/cljs/core.cljs
    Compiling release/clojure/data.cljs
    Building module :cljs-base
      adding entry (cljs.core)
      adding entry (mori)
    Building module :extra
      adding entry (mori.extra)
      adding entry (clojure.set)
      adding entry (clojure.data)
      adding entry (cljs.reader)
      module :extra depends on :cljs-base
    Building module :mutable
      adding entry (mori.mutable)
      module :mutable depends on :cljs-base
    Adding remaining namespaces to :cljs-base
      adding entry (goog)
      adding entry [goog.string goog.string.Unicode]
      adding entry [goog.object]
      adding entry [goog.string.StringBuffer]
      adding entry [goog.debug.Error]
      adding entry [goog.dom.NodeType]
      adding entry [goog.asserts goog.asserts.AssertionError]
      adding entry [goog.array goog.array.ArrayLike]
      adding entry (constants-table)
      adding entry (clojure.zip)
    Successfully compiled "/Users/kasuko/Documents/Projects/mori/target/cljsbuild-main.js" in 12.099 seconds.
    Finalizing mori.js
    cat: mori.bare.js: No such file or directory
    
    opened by Ionshard 1
  • Essential vector methods missing?

    Essential vector methods missing?

    Hello, I've started using mori on an experimental project a few weeks ago and all was good until I wanted to do some updates on a vector.

    1 . The first thing I tried to do was to update an object from the vector, based on its ID Since there is no update method for vectors, I'd naturally do something like:

    • find the element's index
    • update/replace the element at index

    ...but there is no "findIndex" method. the closest thing to a real find method is "some", but it doesn't give you the index, just the value.

    I ended up doing:

    files: mori.map(
            file => (
              file.getId() === fileInfo.id ?
                file.setInfo(fileInfo) :
                file
            ),
            state.files
          )
    

    but this seems kind of inefficient.

    2 . Add some elements to the vector, replacing the existing ones with the same ID (but maybe different values in other properties)

    This was very tedious, and I ended up converting the vector to array, doing the operations and then converting the resulting array back to vector. Again, inefficient.

    I tried using a set, but both elements with same ID ended up in the result and there's no way of defining the equality check predicate.

    Am I missing something here or mori's missing some pretty important things?

    opened by tszekely-spotcap 1
Owner
David Nolen
Lead developer of ClojureScript. Software Engineer at https://vouch.io
David Nolen
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
🛠️construct-js is a library for creating byte level data structures.

??️construct-js is a library for creating byte level data structures.

Francis Stokes 1.3k Dec 14, 2022
A complete, fully tested and documented data structure library written in pure JavaScript.

Buckets A JavaScript Data Structure Library Buckets is a complete, fully tested and documented data structure library written in pure JavaScript. Incl

Mauricio 1.2k Jan 4, 2023
A simulation data generator

Mock.js Mock.js is a simulation data generator to help the front-end to develop and prototype separate from the back-end progress and reduce some mono

高云 墨智 18.7k Jan 4, 2023
mutate a copy of data without changing the original source

immutability-helper Mutate a copy of data without changing the original source Setup via NPM npm install immutability-helper --save This is a drop-in

Moshe Kolodny 5.1k Dec 29, 2022
JSON-Schema + fake data generators

Use JSON Schema along with fake generators to provide consistent and meaningful fake data for your system. What's next? Breaking-changes towards v0.5.

JSON Schema Faker 2.9k Jan 4, 2023
HashMap JavaScript class for Node.js and the browser. The keys can be anything and won't be stringified

HashMap Class for JavaScript Installation Using npm: $ npm install hashmap Using bower: $ bower install hashmap You can download the last stable ver

Ariel Flesler 381 Sep 24, 2022
A tiny JavaScript utility to access deep properties using a path (for Node and the Browser)

object-path Access deep properties using a path Changelog 0.11.5 SECURITY FIX. Fix a prototype pollution vulnerability in the set() function when usin

Mario Casciaro 1k Dec 29, 2022
An isomorphic and configurable javascript utility for objects deep cloning that supports circular references.

omniclone An isomorphic and configurable javascript function for object deep cloning. omniclone(source [, config, [, visitor]]); Example: const obj =

Andrea Simone Costa 184 May 5, 2022
Hjson for JavaScript

hjson-js Hjson, a user interface for JSON JSON is easy for humans to read and write... in theory. In practice JSON gives us plenty of opportunities to

Hjson 387 Dec 20, 2022
⚡️The Fullstack React Framework — built on Next.js

The Fullstack React Framework "Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails Read the Documentation “Zero-API” data layer lets y

⚡️Blitz 12.5k Jan 4, 2023
A table component for your Mantine data-rich applications, supporting asynchronous data loading, column sorting, custom cell data rendering, row context menus, dark theme, and more.

Mantine DataTable A "dark-theme aware" table component for your Mantine UI data-rich applications, featuring asynchronous data loading support, pagina

Ionut-Cristian Florescu 331 Jan 4, 2023
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Dec 31, 2022
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

BrowstorJS ?? ?? ?? Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all

Nullix 8 Aug 5, 2022
A high-resolution local database that uses precise algorithms to easily record data in local files within a project with persistent JSON and YAML support designed to be easy to set up and use

About A high-resolution local database that uses precise algorithms to easily record data in local files within a project with persistent JSON and YML

Shuruhatik 5 Dec 28, 2022
danfo.js is an open source, JavaScript library providing high performance, intuitive, and easy to use data structures for manipulating and processing structured data.

Danfojs: powerful javascript data analysis toolkit What is it? Danfo.js is a javascript package that provides fast, flexible, and expressive data stru

JSdata 4k Dec 29, 2022
Superlight vanilla javascript plugin, for modern web dropdowns. Supporting multi-options, search and images. Designed to be seamlessly themed

Superlight vanilla javascript dropdowns by LCweb Need to jump off of jQuery (or any other) dependency? Other packages are too heavy to just tweak sele

Luca 10 Dec 26, 2022
The missing Javascript smart persistent layer

Basil.js The missing Javascript smart persistence layer. Unified localstorage, cookie and session storage JavaScript API. Philosophy Basil aims to eas

Wisembly 2k Dec 2, 2022
Simple, lightweight, persistent two-way databinding

way.js Simple, lightweight, persistent, framework-agnostic two-way databinding Javascript library. With no to little JS code to write. And no dependen

gwendall 2.9k Dec 30, 2022