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

can-derive

v0.0.16

Published

Derive a list/map from another via live binding

Downloads

25

Readme

can-derive

can-derive is a plugin that creates observable filtered lists that stay up-to-date with a source list.

For example, a todo list might contain todo objects with a completed property. Traditionally can.List.filter enables you to create a new can.List containing only the "completed" todo objects. However, if the source list were to change in any way - for instance via an "add" or "remove" - the returned can.List may become an innaccurate representation of the source list. The same filtered list of "completed" todo objects created with can-derive's can.List.dFilter would always be an accurate representation of with the source, no matter how it was manipulated.

can-derive is ideal for cases where the source list contains at least 10 items and is expected to be "changed" frequently (3 or more times).

See it in action on JSBin.

Install

Use npm to install can-derive:

npm install can-derive --save

Use

Use require in Node/Browserify workflows to import the can-derive plugin like:

require('can-derive');

Use define, require, or import in StealJS workflows to import the can-derive plugin like:

import 'can-derive';

Once you've imported can-derive into your project, simply use can.List.dFilter to generate a derived list based on a predicate function. The following example derives a list of completed items from a todo list:

var sourceList = new can.List([
    { name: 'Hop', complete: true },
    { name: 'Skip', complete: false },
    //...
]);

var completed = sourceList.filter(function(todo) {
    return todo.attr("complete") === true;
});

Any changes to sourceList will automatically update the derived completed list:

completed.bind('add', function(ev, newItems) {
    console.log(newItems.length, 'item(s) added');
});

sourceList.push({ name: 'Jump', complete: true },
    { name: 'Sleep', complete: false }); //-> "1 item(s) added"

With can.Map.define

If you're using the can.Map.define plugin, you can define a derived list like so:

{
    define: {
        todos: {
            Value: can.List
        },
        completedTodos: {
            get: function() {
                return this.attr('todos').dFilter(function(todo){
                    return todo.attr('complete') === true;
                });
            }
        }
    }
}

Note: The can-derive plugin ensures that the define plugin's get method will not observe "length" like it would a traditional can.List when calling .filter().

Accessing FilteredList values

Unlike can.List and Array, indexes of a FilteredList cannot be accessed using bracket notation:

filteredList[1]; //-> undefined

To access a FilteredList's values, use .attr():

filteredList.attr(); //-> ["a", "b", "c"]
filteredList.attr(0); //-> "a"
filteredList.attr(1); //-> "b"
filteredList.attr(2); //-> "c"
filteredList.attr('length'); //-> 3

This is due to the fact that a FilteredList inherits a can.RBTreeList and stores its values in a Red-black tree for performance - rather than a series of numeric keys.

API

can.List

.dFilter()

sourceList.filter(predicateFn) -> FilteredList

Similar to .filter() except that the returned FilteredList is bound to sourceList.

Returns a FilteredList.

FilteredList

Inherited can.RBTreeList methods

Since FilteredList inherits from can.RBTreeList, the following methods are available:

Disabled can.RBTreeList methods

A FilteredList is bound to its source list and manipulted as it changes. Because of this, it is read-only and the following can.RBTreeList methods are disabled:

  • .push()
  • .pop()
  • .removeAttr()
  • .replace()
  • .shift()
  • .splice()
  • .unshift()

Performance

can-derive optimizes for insertions and removals, completing them in O(log n) time. This means that changes to the source list will automatically update the derived list in O(log n) time, compared to the standard O(n) time you would expect in other implementations.

It does this by:

  • Keeping the derived list in a Red-black tree
  • Listening for additions or removals in the source list
  • Listening for predicate function result changes for any item

This algorithm was originally discussed in [this StackExchange thread](http://cs.stackexchange.com/questions/43447/order-preserving-update-of-a -sublist-of-a-list-of-mutable-objects-in-sublinear-t/44502#44502).

When to Use

In general, it is preferable to use can-derive over alternative approaches when:

  • Your source list contains 10 or more elements
  • You need to know how the filtered list changed, for instance when rendering in the DOM.

Contributing

To set up your dev environment:

  1. Clone and fork this repo.
  2. Run npm install.
  3. Open list/test.html in your browser. Everything should pass.
  4. Run npm test. Everything should pass.
  5. Run npm run-script build. Everything should build ok