Frptools uses a hash function now instead of a comparator
This commit is contained in:
parent
46fdb975c5
commit
0b26e63061
@ -51,22 +51,24 @@ const remainingSubscriptionCount = unsubscribe();
|
|||||||
inViewport.unsubscribeAll(); // Call unsubscribeAll to remove child property/computed subscriptions.
|
inViewport.unsubscribeAll(); // Call unsubscribeAll to remove child property/computed subscriptions.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Provide a comparator for complex types
|
### Provide a hash function for complex types
|
||||||
|
|
||||||
When storing a type that is not determined to be equal with simple equality (===), provide a
|
When storing a type that is not determined to be equal with simple equality (===), provide a hash
|
||||||
function to determine in the new provided value should be propagated to dependents.
|
function to be used for simple comparison to determine if the new provided value should be
|
||||||
|
propagated to dependents.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function setEquals(a, b) {
|
function hashSet(_a) {
|
||||||
return (
|
if (_a instanceof Set) {
|
||||||
a instanceof Set &&
|
return Array.from(_a.keys())
|
||||||
b instanceof Set &&
|
.sort()
|
||||||
[...a].reduce((acc, d) => acc && b.has(d), true) &&
|
.map(k => `${(typeof k).substr(0, 1)}:${encodeURIComponent(k)}/`)
|
||||||
[...b].reduce((acc, d) => acc && a.has(d), true)
|
.join('?');
|
||||||
);
|
}
|
||||||
|
return _a;
|
||||||
}
|
}
|
||||||
|
|
||||||
const a = prop(new Set([1, 2]), setEquals);
|
const a = prop(new Set([1, 2]), hashSet);
|
||||||
```
|
```
|
||||||
|
|
||||||
# [computed](./src/computed.js)
|
# [computed](./src/computed.js)
|
||||||
@ -137,28 +139,30 @@ showDialog.detach(); // Call detach to remove this computed from the logic tree.
|
|||||||
showDialog.unsubscribeAll(); // Call unsubscribeAll to remove child property/computed subscriptions.
|
showDialog.unsubscribeAll(); // Call unsubscribeAll to remove child property/computed subscriptions.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Provide a comparator for complex types
|
### Provide a hash function for complex types
|
||||||
|
|
||||||
When the computed result is a type that is not determined to be equal with simple equality (===),
|
When the computed result is a type that is not determined to be equal with simple equality (===),
|
||||||
provide a function to determine in the new provided value should be propagated to dependents.
|
provide a hash function to be used for simple comparison to determine if the new provided value
|
||||||
|
should be propagated to dependents.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function setEquals(a, b) {
|
function hashSet(_a) {
|
||||||
return (
|
if (_a instanceof Set) {
|
||||||
a instanceof Set &&
|
return Array.from(_a.keys())
|
||||||
b instanceof Set &&
|
.sort()
|
||||||
[...a].reduce((acc, d) => acc && b.has(d), true) &&
|
.map(k => `${(typeof k).substr(0, 1)}:${encodeURIComponent(k)}/`)
|
||||||
[...b].reduce((acc, d) => acc && a.has(d), true)
|
.join('?');
|
||||||
);
|
}
|
||||||
|
return _a;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _intersection(a, b) {
|
function _intersection(a, b) {
|
||||||
return new Set([...a].filter(x => b.has(x)));
|
return new Set([...a].filter(x => b.has(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const a = prop(new Set([1, 2]), setEquals);
|
const a = prop(new Set([1, 2]), hashSet);
|
||||||
const b = prop(new Set([2, 3]), setEquals);
|
const b = prop(new Set([2, 3]), hashSet);
|
||||||
const intersection = computed(_intersection, [a, b], setEquals);
|
const intersection = computed(_intersection, [a, b], hashSet);
|
||||||
```
|
```
|
||||||
|
|
||||||
# [bundle](./src/bundle.js)
|
# [bundle](./src/bundle.js)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "frptools",
|
"name": "frptools",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"description": "Observable Property and Computed data streams",
|
"description": "Observable Property and Computed data streams",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
const { prop, computed } = require('../lib/index.js');
|
const { prop, computed } = require('../lib/index.js');
|
||||||
|
const { hashSet } = require('../lib/util.js');
|
||||||
|
|
||||||
describe('computed', () => {
|
describe('computed', () => {
|
||||||
const add = (a, b) => a + b;
|
const add = (a, b) => a + b;
|
||||||
@ -157,15 +158,6 @@ describe('computed', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses a comparator', () => {
|
it('uses a comparator', () => {
|
||||||
function setEquals(a, b) {
|
|
||||||
return (
|
|
||||||
a instanceof Set &&
|
|
||||||
b instanceof Set &&
|
|
||||||
[...a].reduce((acc, d) => acc && b.has(d), true) &&
|
|
||||||
[...b].reduce((acc, d) => acc && a.has(d), true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let runCount = 0;
|
let runCount = 0;
|
||||||
|
|
||||||
function intersection(a, b) {
|
function intersection(a, b) {
|
||||||
@ -173,9 +165,9 @@ describe('computed', () => {
|
|||||||
return new Set([...a].filter(x => b.has(x)));
|
return new Set([...a].filter(x => b.has(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const a = prop(new Set([1, 2]), setEquals);
|
const a = prop(new Set([1, 2]), hashSet);
|
||||||
const b = prop(new Set([2, 3]), setEquals);
|
const b = prop(new Set([2, 3]), hashSet);
|
||||||
const ABintersection = computed(intersection, [a, b], setEquals);
|
const ABintersection = computed(intersection, [a, b], hashSet);
|
||||||
|
|
||||||
expect(runCount).toEqual(0);
|
expect(runCount).toEqual(0);
|
||||||
expect([...ABintersection()]).toEqual([2]);
|
expect([...ABintersection()]).toEqual([2]);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
const { prop } = require('../lib/index.js');
|
const { prop } = require('../lib/index.js');
|
||||||
|
const { hashSet } = require('../lib/util.js');
|
||||||
|
|
||||||
describe('A property', () => {
|
describe('A property', () => {
|
||||||
it('returns its initialized value', () => {
|
it('returns its initialized value', () => {
|
||||||
@ -62,19 +63,10 @@ describe('A property', () => {
|
|||||||
expect(runCount).toEqual(3);
|
expect(runCount).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses a comparator', () => {
|
it('uses a hash function', () => {
|
||||||
function setEquals(a, b) {
|
|
||||||
return (
|
|
||||||
a instanceof Set &&
|
|
||||||
b instanceof Set &&
|
|
||||||
[...a].reduce((acc, d) => acc && b.has(d), true) &&
|
|
||||||
[...b].reduce((acc, d) => acc && a.has(d), true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let runCount = 0;
|
let runCount = 0;
|
||||||
|
|
||||||
const a = prop(new Set([1, 2]), setEquals);
|
const a = prop(new Set([1, 2]), hashSet);
|
||||||
a.subscribe(() => (runCount += 1));
|
a.subscribe(() => (runCount += 1));
|
||||||
expect([...a()]).toEqual([1, 2]);
|
expect([...a()]).toEqual([1, 2]);
|
||||||
expect(runCount).toEqual(0);
|
expect(runCount).toEqual(0);
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { eq } from './util.js';
|
import { id } from './util.js';
|
||||||
|
|
||||||
export function computed(fn, dependencies = [], comparator = eq) {
|
export function computed(fn, dependencies = [], hash = id) {
|
||||||
const subscribers = new Set();
|
const subscribers = new Set();
|
||||||
const dependents = new Set();
|
const dependents = new Set();
|
||||||
let isDirty = true;
|
let isDirty = true;
|
||||||
let val;
|
let val;
|
||||||
|
let id;
|
||||||
|
|
||||||
// Receive dirty flag from parent logic node (dependency). Pass it down.
|
// Receive dirty flag from parent logic node (dependency). Pass it down.
|
||||||
function _computedDirtyReporter(_, skipPropagation) {
|
function _computedDirtyReporter(_, skipPropagation) {
|
||||||
@ -25,7 +26,9 @@ export function computed(fn, dependencies = [], comparator = eq) {
|
|||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
const newVal = fn.apply(null, dependencies.map(runParam));
|
const newVal = fn.apply(null, dependencies.map(runParam));
|
||||||
isDirty = false;
|
isDirty = false;
|
||||||
if (!comparator(val, newVal)) {
|
const newId = hash(newVal);
|
||||||
|
if (id !== newId) {
|
||||||
|
id = newId;
|
||||||
val = newVal;
|
val = newVal;
|
||||||
subscribers.forEach(s => s(val));
|
subscribers.forEach(s => s(val));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { eq } from './util.js';
|
import { id } from './util.js';
|
||||||
|
|
||||||
export function prop(store, comparator = eq) {
|
export function prop(store, hash = id) {
|
||||||
const subscribers = new Set();
|
const subscribers = new Set();
|
||||||
|
let id = hash(store);
|
||||||
|
|
||||||
const accessor = function _prop(newVal) {
|
const accessor = function _prop(newVal) {
|
||||||
if (newVal !== undefined && !comparator(store, newVal)) {
|
const newId = hash(newVal);
|
||||||
|
if (newVal !== undefined && id !== newId) {
|
||||||
|
id = newId;
|
||||||
store = newVal;
|
store = newVal;
|
||||||
subscribers.forEach(s => s(store));
|
subscribers.forEach(s => s(store));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,11 @@
|
|||||||
export const eq = (a, b) => a === b;
|
export const id = a => a;
|
||||||
|
|
||||||
|
export function hashSet(_a) {
|
||||||
|
if (_a instanceof Set) {
|
||||||
|
return Array.from(_a.keys())
|
||||||
|
.sort()
|
||||||
|
.map(k => `${(typeof k).substr(0, 1)}:${encodeURIComponent(k)}/`)
|
||||||
|
.join('?');
|
||||||
|
}
|
||||||
|
return _a;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user