Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

formula-one

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

formula-one - npm Package Compare versions

Comparing version 0.9.0-alpha.4 to 0.9.0-alpha.5

8

CHANGELOG.md

@@ -11,3 +11,3 @@ # Changelog

- Add `addFields` and `filterFields` array manipulators. These are currently necessary due to the non-atomic nature of the current `addField` and `removeField` manipulators. They will be made atomic in a future version.
- Add `addFields`, `filterFields`, and `modifyFields` array manipulators. These are currently necessary due to the non-atomic nature of the current `addField` and `removeField` manipulators. They will be made atomic in a future version.

@@ -25,2 +25,8 @@ The API is:

filterFields: (predicate: (item: E, index: number) => boolean) => void
// A way to simultaneously add and remove fields from an ArrayField<E>
modifyFields: ({
insertSpans: $ReadOnlyArray<Span<E>,
filterPredicate: (item: E, index: number) => boolean
}) => void
```

@@ -27,0 +33,0 @@

@@ -152,7 +152,12 @@ "use strict";

var _unzip = (0, _array.unzip)(zipped.filter(function (_ref4, i) {
var _unzip = (0, _array.unzip)(zipped.filter(function (_ref4, i, arr) {
var _ref5 = _slicedToArray(_ref4, 1),
value = _ref5[0];
return predicate(value, i);
return predicate(value, i, arr.map(function (_ref6) {
var _ref7 = _slicedToArray(_ref6, 1),
v = _ref7[0];
return v;
}));
})),

@@ -166,3 +171,6 @@ _unzip2 = _slicedToArray(_unzip, 2),

_this.props.link.onChange((0, _formState3.validate)(_this.props.validation, (0, _formState3.setChanged)((0, _formState3.setTouched)([newValue, newTree]))));
}, _this._removeChildField = function (index) {
}, _this._modifyChildFields = function (_ref8) {
var insertSpans = _ref8.insertSpans,
filterPredicate = _ref8.filterPredicate;
var _this$props$link$form5 = _slicedToArray(_this.props.link.formState, 2),

@@ -172,7 +180,47 @@ oldValue = _this$props$link$form5[0],

var newValue = (0, _array.removeAt)(index, oldValue);
var newTree = (0, _shapedTree.dangerouslySetChildren)((0, _array.removeAt)(index, (0, _shapedTree.shapedArrayChildren)(oldTree)), oldTree);
var cleanNode = {
errors: _types.cleanErrors,
meta: _types.cleanMeta
};
// TODO(zach): there's a less complicated, more functorial way to do this
// augment, then unaugment
var zipped = (0, _array.zip)(oldValue, (0, _shapedTree.shapedArrayChildren)(oldTree));
// augment the spans with fresh nodes
var augmentedSpans = insertSpans !== undefined ? insertSpans.map(function (_ref9) {
var _ref10 = _slicedToArray(_ref9, 2),
index = _ref10[0],
contents = _ref10[1];
return [index, contents.map(function (v) {
return [v, (0, _shapedTree.treeFromValue)(v, cleanNode)];
})];
}) : undefined;
// augment the predicate to work on formstates
var augmentedPredicate = filterPredicate !== undefined ? function (_ref11, i, arr) {
var _ref12 = _slicedToArray(_ref11, 2),
v = _ref12[0],
_ = _ref12[1];
return filterPredicate(v, i, arr.map(function (_ref13) {
var _ref14 = _slicedToArray(_ref13, 2),
v = _ref14[0],
_ = _ref14[1];
return v;
}));
} : undefined;
var _unzip3 = (0, _array.unzip)((0, _array.modify)({ insertSpans: augmentedSpans, filterPredicate: augmentedPredicate }, zipped)),
_unzip4 = _slicedToArray(_unzip3, 2),
newValue = _unzip4[0],
newChildren = _unzip4[1];
var newTree = (0, _shapedTree.dangerouslySetChildren)(newChildren, oldTree);
_this.props.link.onChange((0, _formState3.validate)(_this.props.validation, (0, _formState3.setChanged)((0, _formState3.setTouched)([newValue, newTree]))));
}, _this._moveChildField = function (from, to) {
}, _this._removeChildField = function (index) {
var _this$props$link$form6 = _slicedToArray(_this.props.link.formState, 2),

@@ -182,2 +230,11 @@ oldValue = _this$props$link$form6[0],

var newValue = (0, _array.removeAt)(index, oldValue);
var newTree = (0, _shapedTree.dangerouslySetChildren)((0, _array.removeAt)(index, (0, _shapedTree.shapedArrayChildren)(oldTree)), oldTree);
_this.props.link.onChange((0, _formState3.validate)(_this.props.validation, (0, _formState3.setChanged)((0, _formState3.setTouched)([newValue, newTree]))));
}, _this._moveChildField = function (from, to) {
var _this$props$link$form7 = _slicedToArray(_this.props.link.formState, 2),
oldValue = _this$props$link$form7[0],
oldTree = _this$props$link$form7[1];
var newValue = (0, _array.moveFromTo)(from, to, oldValue);

@@ -216,4 +273,4 @@ var newTree = (0, _shapedTree.dangerouslySetChildren)((0, _array.moveFromTo)(from, to, (0, _shapedTree.shapedArrayChildren)(oldTree)), oldTree);

value: function forceChildRemount() {
this.setState(function (_ref6) {
var nonce = _ref6.nonce;
this.setState(function (_ref15) {
var nonce = _ref15.nonce;
return { nonce: nonce + 1 };

@@ -238,3 +295,4 @@ });

addFields: this._addChildFields,
filterFields: this._filterChildFields
filterFields: this._filterChildFields,
modifyFields: this._modifyChildFields
}, {

@@ -241,0 +299,0 @@ touched: (0, _formState3.getExtras)(formState).meta.touched,

@@ -488,3 +488,3 @@ "use strict";

});
it("validates after entry is added", function () {
it("validates after fields are added", function () {
var formStateValue = ["one", "two", "three"];

@@ -520,3 +520,3 @@ var formState = (0, _tools.mockFormState)(formStateValue);

describe("filterFields", function () {
it("exposes addFields to add an entry", function () {
it("exposes filterFields to filter entries", function () {
var formStateValue = ["one", "two", "three"];

@@ -539,3 +539,3 @@ var formState = (0, _tools.mockFormState)(formStateValue);

});
it("validates after entry is added", function () {
it("validates after fields are filtered", function () {
var formStateValue = ["one", "two", "three", "four", "five"];

@@ -573,2 +573,56 @@ var formState = (0, _tools.mockFormState)(formStateValue);

});
describe("modifyFields", function () {
it("exposes modifyFields to add and remove entries atomically", function () {
var formStateValue = ["one", "two", "three"];
var formState = (0, _tools.mockFormState)(formStateValue);
var link = (0, _tools.mockLink)(formState);
var renderFn = jest.fn(function () {
return null;
});
_reactTestRenderer2.default.create(React.createElement(
_ArrayField2.default,
{ link: link },
renderFn
));
expect(renderFn).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
modifyFields: expect.any(Function)
}), expect.anything());
});
it("validates after fields are modified", function () {
var formStateValue = ["one", "two", "three"];
var formState = (0, _tools.mockFormState)(formStateValue);
var link = (0, _tools.mockLink)(formState);
var renderFn = jest.fn(function () {
return null;
});
var validation = jest.fn(function () {
return ["an error"];
});
_reactTestRenderer2.default.create(React.createElement(
_ArrayField2.default,
{ validation: validation, link: link },
renderFn
));
expect(validation).toHaveBeenCalledTimes(1);
var _renderFn$mock$calls$6 = _slicedToArray(renderFn.mock.calls[0], 2),
_ = _renderFn$mock$calls$6[0],
modifyFields = _renderFn$mock$calls$6[1].modifyFields;
modifyFields({
insertSpans: [[0, ["start"]], [2, ["middle", "content"]]],
filterPredicate: function filterPredicate(v) {
return v !== "one";
}
});
expect(validation).toHaveBeenCalledTimes(2);
expect(validation).toHaveBeenLastCalledWith(["start", "two", "middle", "content", "three"]);
});
});
});

@@ -575,0 +629,0 @@

@@ -21,2 +21,122 @@ "use strict";

describe("modify", function () {
it("is identity when given nothing to insert or remove", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({}, [])).toEqual([]);
expect((0, _array.modify)({}, a)).toEqual(a);
});
describe("inserting spans", function () {
it("is identity when given no spans", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({ insertSpans: [] }, [])).toEqual([]);
expect((0, _array.modify)({ insertSpans: [] }, a)).toEqual(a);
});
it("inserts a single span", function () {
var a = ["one", "two", "three"];
var s = [1, ["hello", "world"]];
expect((0, _array.modify)({ insertSpans: [s] }, a)).toEqual(["one", "hello", "world", "two", "three"]);
});
it("inserts a span at the end of the array", function () {
var a = ["one", "two", "three"];
var s = [3, ["the", "end"]];
expect((0, _array.modify)({ insertSpans: [s] }, a)).toEqual(["one", "two", "three", "the", "end"]);
});
it("errors if two spans with the same index are provided", function () {
var redundantSpans = [[0, ["one", "thing"]], [0, ["and", "another"]]];
expect(function () {
(0, _array.modify)({ insertSpans: redundantSpans }, []);
}).toThrowError("at the same index");
});
it("inserts multiple spans in the correct place", function () {
var s0 = [1, ["uno"]];
var s1 = [2, ["dos"]];
var a = ["one", "two", "three"];
expect((0, _array.modify)({ insertSpans: [s0, s1] }, a)).toEqual(["one", "uno", "two", "dos", "three"]);
});
});
describe("filtering", function () {
it("is identity when given (const true)", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({ filterPredicate: function filterPredicate() {
return true;
} }, [])).toEqual([]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate() {
return true;
} }, a)).toEqual(a);
});
it("removes everything when given (const false)", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({ filterPredicate: function filterPredicate() {
return false;
} }, [])).toEqual([]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate() {
return false;
} }, a)).toEqual([]);
});
it("removes things based on values", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({ filterPredicate: function filterPredicate(s) {
return s === "one";
} }, [])).toEqual([]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate(s) {
return s === "one";
} }, a)).toEqual(["one"]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate(s) {
return s !== "one";
} }, a)).toEqual(["two", "three"]);
});
it("removes things based on index", function () {
var a = ["one", "two", "three"];
expect((0, _array.modify)({ filterPredicate: function filterPredicate(_, i) {
return i === 1;
} }, [])).toEqual([]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate(_, i) {
return i === 1;
} }, a)).toEqual(["two"]);
expect((0, _array.modify)({ filterPredicate: function filterPredicate(_, i) {
return i !== 1;
} }, a)).toEqual(["one", "three"]);
});
it("removes things based on the whole array", function () {
var a = ["one", "two", "three"];
var pred = jest.fn();
(0, _array.modify)({ filterPredicate: pred }, a);
expect(pred).toHaveBeenCalledWith(expect.anything(), expect.anything(), a);
expect((0, _array.modify)({
filterPredicate: function filterPredicate(_0, i, arr) {
return i + 1 < arr.length && arr[i + 1] === "three";
}
}, a)).toEqual(["two"]);
});
});
describe("inserting and filtering simultaneously", function () {
it("can insert and remove simultaneously", function () {
expect((0, _array.modify)({
insertSpans: [[0, ["front"]], [3, ["middle", "content"]]],
filterPredicate: function filterPredicate(_, i) {
return !(i === 0 || i === 2 || i === 3);
}
}, ["one", "two", "three", "four", "five"])).toEqual(["front", "two", "middle", "content", "five"]);
});
});
});
describe("insertSpans", function () {

@@ -23,0 +143,0 @@ it("is identity when given no spans", function () {

86

dist/utils/array.js

@@ -13,2 +13,3 @@ "use strict";

exports.moveFromTo = moveFromTo;
exports.modify = modify;
exports.insertSpans = insertSpans;

@@ -40,44 +41,69 @@ exports.zipWith = zipWith;

function insertSpans(spans, arr) {
// no duplicated indices are allowed, ECMAScript Array.sort is not stable by spec
var indexSet = new Set(spans.map(function (_ref) {
var _ref2 = _slicedToArray(_ref, 1),
i = _ref2[0];
function modify(_ref, arr) {
var insertSpans = _ref.insertSpans,
filterPredicate = _ref.filterPredicate;
return i;
}));
if (indexSet.size !== spans.length) {
throw new Error("You cannot insert two spans at the same index. Combine the values of the spans.");
}
var sortedSpans = [];
if (insertSpans !== undefined) {
// no duplicated indices are allowed, ECMAScript Array.sort is not stable by spec
var indexSet = new Set(insertSpans.map(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 1),
i = _ref3[0];
// sort spans by insertion position
var spansCopy = [].concat(_toConsumableArray(spans));
spansCopy.sort(function (_ref3, _ref4) {
var _ref6 = _slicedToArray(_ref3, 1),
i = _ref6[0];
return i;
}));
if (indexSet.size !== insertSpans.length) {
throw new Error("You cannot insert two spans at the same index. Combine the values of the spans.");
}
var _ref5 = _slicedToArray(_ref4, 1),
j = _ref5[0];
// sort spans by insertion position
sortedSpans = [].concat(_toConsumableArray(insertSpans));
sortedSpans.sort(function (_ref4, _ref5) {
var _ref7 = _slicedToArray(_ref4, 1),
i = _ref7[0];
return i - j;
});
var _ref6 = _slicedToArray(_ref5, 1),
j = _ref6[0];
return i - j;
});
}
// The next span to insert
var nextSpanIndex = 0;
// build the new array in one pass
var ret = [];
var lastIndexInsertedAt = 0;
spansCopy.forEach(function (_ref7) {
var _ref8 = _slicedToArray(_ref7, 2),
index = _ref8[0],
contents = _ref8[1];
for (var i = 0; i < arr.length; i += 1) {
if (nextSpanIndex < sortedSpans.length) {
var _sortedSpans$nextSpan = _slicedToArray(sortedSpans[nextSpanIndex], 2),
index = _sortedSpans$nextSpan[0],
contents = _sortedSpans$nextSpan[1];
// All the content before this
ret = ret.concat(arr.slice(lastIndexInsertedAt, index));
ret = ret.concat(contents);
lastIndexInsertedAt = index;
});
ret = ret.concat(arr.slice(lastIndexInsertedAt));
if (index === i) {
ret = ret.concat(contents);
nextSpanIndex += 1;
}
}
if (filterPredicate === undefined || filterPredicate(arr[i], i, arr)) {
ret.push(arr[i]);
}
}
// insert spans after the end of the array
for (var _i = nextSpanIndex; _i < sortedSpans.length; _i += 1) {
var _sortedSpans$_i = _slicedToArray(sortedSpans[_i], 2),
_ = _sortedSpans$_i[0],
_contents = _sortedSpans$_i[1];
ret = ret.concat(_contents);
}
return ret;
}
function insertSpans(spans, arr) {
return modify({ insertSpans: spans }, arr);
}
// Strict on length

@@ -84,0 +110,0 @@ function zipWith(f, left, right) {

{
"name": "formula-one",
"version": "0.9.0-alpha.4",
"version": "0.9.0-alpha.5",
"description": "Strongly-typed React form state management",

@@ -5,0 +5,0 @@ "author": "Zach Gotsch",

@@ -289,3 +289,3 @@ # formula-one

| touched | `boolean` | Whether the field has been touched (blurred or changed) |
| ch}anged | `boolean` | Whether the field has been changed |
| changed | `boolean` | Whether the field has been changed |
| shouldShowErrors | `boolean` | Whether errors should be shown according to the current feedback strategy |

@@ -292,0 +292,0 @@ | unfilteredErrors | `$ReadOnlyArray<string>` | All validation errors for the current field. (This differs from the `errors` argument in `<Field>`, since the `errors` argument in `<Field>` will be empty if `shouldShowErrors` is false) |

@@ -28,2 +28,3 @@ // @flow strict

insertSpans,
modify,
zip,

@@ -62,3 +63,9 @@ unzip,

addFields: (spans: $ReadOnlyArray<[number, $ReadOnlyArray<E>]>) => void,
filterFields: (predicate: (E, number) => boolean) => void,
filterFields: (
predicate: (E, number, $ReadOnlyArray<E>) => boolean
) => void,
modifyFields: ({
insertSpans?: $ReadOnlyArray<[number, $ReadOnlyArray<E>]>,
filterPredicate?: (E, number, $ReadOnlyArray<E>) => boolean,
}) => void,
},

@@ -244,3 +251,3 @@ additionalInfo: AdditionalRenderInfo<Array<E>>

_filterChildFields: (
predicate: (E, number) => boolean
predicate: (E, number, $ReadOnlyArray<E>) => boolean
) => void = predicate => {

@@ -251,3 +258,5 @@ const [oldValue, oldTree] = this.props.link.formState;

const [newValue, newChildren] = unzip(
zipped.filter(([value], i) => predicate(value, i))
zipped.filter(([value], i, arr) =>
predicate(value, i, arr.map(([v]) => v))
)
);

@@ -264,2 +273,48 @@ const newTree = dangerouslySetChildren(newChildren, oldTree);

_modifyChildFields: ({
insertSpans?: $ReadOnlyArray<[number, $ReadOnlyArray<E>]>,
filterPredicate?: (E, number, $ReadOnlyArray<E>) => boolean,
}) => void = ({insertSpans, filterPredicate}) => {
const [oldValue, oldTree] = this.props.link.formState;
const cleanNode = {
errors: cleanErrors,
meta: cleanMeta,
};
// TODO(zach): there's a less complicated, more functorial way to do this
// augment, then unaugment
const zipped = zip(oldValue, shapedArrayChildren(oldTree));
// augment the spans with fresh nodes
const augmentedSpans =
insertSpans !== undefined
? insertSpans.map(([index, contents]) => [
index,
contents.map(v => [v, treeFromValue(v, cleanNode)]),
])
: undefined;
// augment the predicate to work on formstates
const augmentedPredicate =
filterPredicate !== undefined
? ([v, _], i, arr) => filterPredicate(v, i, arr.map(([v, _]) => v))
: undefined;
const [newValue, newChildren] = unzip(
modify(
{insertSpans: augmentedSpans, filterPredicate: augmentedPredicate},
zipped
)
);
const newTree = dangerouslySetChildren(newChildren, oldTree);
this.props.link.onChange(
validate(
this.props.validation,
setChanged(setTouched([newValue, newTree]))
)
);
};
_removeChildField = (index: number) => {

@@ -318,2 +373,3 @@ const [oldValue, oldTree] = this.props.link.formState;

filterFields: this._filterChildFields,
modifyFields: this._modifyChildFields,
},

@@ -320,0 +376,0 @@ {

@@ -379,3 +379,3 @@ // @flow

});
it("validates after entry is added", () => {
it("validates after fields are added", () => {
const formStateValue = ["one", "two", "three"];

@@ -412,3 +412,3 @@ const formState = mockFormState(formStateValue);

describe("filterFields", () => {
it("exposes addFields to add an entry", () => {
it("exposes filterFields to filter entries", () => {
const formStateValue = ["one", "two", "three"];

@@ -429,3 +429,3 @@ const formState = mockFormState(formStateValue);

});
it("validates after entry is added", () => {
it("validates after fields are filtered", () => {
const formStateValue = ["one", "two", "three", "four", "five"];

@@ -453,2 +453,51 @@ const formState = mockFormState(formStateValue);

});
describe("modifyFields", () => {
it("exposes modifyFields to add and remove entries atomically", () => {
const formStateValue = ["one", "two", "three"];
const formState = mockFormState(formStateValue);
const link = mockLink(formState);
const renderFn = jest.fn(() => null);
TestRenderer.create(<ArrayField link={link}>{renderFn}</ArrayField>);
expect(renderFn).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
modifyFields: expect.any(Function),
}),
expect.anything()
);
});
it("validates after fields are modified", () => {
const formStateValue = ["one", "two", "three"];
const formState = mockFormState(formStateValue);
const link = mockLink(formState);
const renderFn = jest.fn(() => null);
const validation = jest.fn(() => ["an error"]);
TestRenderer.create(
<ArrayField validation={validation} link={link}>
{renderFn}
</ArrayField>
);
expect(validation).toHaveBeenCalledTimes(1);
const [_, {modifyFields}] = renderFn.mock.calls[0];
modifyFields({
insertSpans: [[0, ["start"]], [2, ["middle", "content"]]],
filterPredicate: v => v !== "one",
});
expect(validation).toHaveBeenCalledTimes(2);
expect(validation).toHaveBeenLastCalledWith([
"start",
"two",
"middle",
"content",
"three",
]);
});
});
});

@@ -455,0 +504,0 @@

// @flow strict
import {moveFromTo, insertSpans} from "../../utils/array";
import {moveFromTo, modify, insertSpans} from "../../utils/array";

@@ -21,2 +21,137 @@ describe("moveFromTo", () => {

describe("modify", () => {
it("is identity when given nothing to insert or remove", () => {
const a = ["one", "two", "three"];
expect(modify({}, [])).toEqual([]);
expect(modify({}, a)).toEqual(a);
});
describe("inserting spans", () => {
it("is identity when given no spans", () => {
const a = ["one", "two", "three"];
expect(modify({insertSpans: []}, [])).toEqual([]);
expect(modify({insertSpans: []}, a)).toEqual(a);
});
it("inserts a single span", () => {
const a = ["one", "two", "three"];
const s = [1, ["hello", "world"]];
expect(modify({insertSpans: [s]}, a)).toEqual([
"one",
"hello",
"world",
"two",
"three",
]);
});
it("inserts a span at the end of the array", () => {
const a = ["one", "two", "three"];
const s = [3, ["the", "end"]];
expect(modify({insertSpans: [s]}, a)).toEqual([
"one",
"two",
"three",
"the",
"end",
]);
});
it("errors if two spans with the same index are provided", () => {
const redundantSpans = [[0, ["one", "thing"]], [0, ["and", "another"]]];
expect(() => {
modify({insertSpans: redundantSpans}, []);
}).toThrowError("at the same index");
});
it("inserts multiple spans in the correct place", () => {
const s0 = [1, ["uno"]];
const s1 = [2, ["dos"]];
const a = ["one", "two", "three"];
expect(modify({insertSpans: [s0, s1]}, a)).toEqual([
"one",
"uno",
"two",
"dos",
"three",
]);
});
});
describe("filtering", () => {
it("is identity when given (const true)", () => {
const a = ["one", "two", "three"];
expect(modify({filterPredicate: () => true}, [])).toEqual([]);
expect(modify({filterPredicate: () => true}, a)).toEqual(a);
});
it("removes everything when given (const false)", () => {
const a = ["one", "two", "three"];
expect(modify({filterPredicate: () => false}, [])).toEqual([]);
expect(modify({filterPredicate: () => false}, a)).toEqual([]);
});
it("removes things based on values", () => {
const a = ["one", "two", "three"];
expect(modify({filterPredicate: s => s === "one"}, [])).toEqual([]);
expect(modify({filterPredicate: s => s === "one"}, a)).toEqual(["one"]);
expect(modify({filterPredicate: s => s !== "one"}, a)).toEqual([
"two",
"three",
]);
});
it("removes things based on index", () => {
const a = ["one", "two", "three"];
expect(modify({filterPredicate: (_, i) => i === 1}, [])).toEqual([]);
expect(modify({filterPredicate: (_, i) => i === 1}, a)).toEqual(["two"]);
expect(modify({filterPredicate: (_, i) => i !== 1}, a)).toEqual([
"one",
"three",
]);
});
it("removes things based on the whole array", () => {
const a = ["one", "two", "three"];
const pred = jest.fn();
modify({filterPredicate: pred}, a);
expect(pred).toHaveBeenCalledWith(
expect.anything(),
expect.anything(),
a
);
expect(
modify(
{
filterPredicate: (_0, i, arr) =>
i + 1 < arr.length && arr[i + 1] === "three",
},
a
)
).toEqual(["two"]);
});
});
describe("inserting and filtering simultaneously", () => {
it("can insert and remove simultaneously", () => {
expect(
modify(
{
insertSpans: [[0, ["front"]], [3, ["middle", "content"]]],
filterPredicate: (_, i) => !(i === 0 || i === 2 || i === 3),
},
["one", "two", "three", "four", "five"]
)
).toEqual(["front", "two", "middle", "content", "five"]);
});
});
});
describe("insertSpans", () => {

@@ -23,0 +158,0 @@ it("is identity when given no spans", () => {

@@ -32,28 +32,52 @@ // @flow strict

export function insertSpans<E>(
spans: $ReadOnlyArray<[number, $ReadOnlyArray<E>]>,
type AddSpan<E> = [number, $ReadOnlyArray<E>];
export function modify<E>(
{
insertSpans,
filterPredicate,
}: {
insertSpans?: $ReadOnlyArray<AddSpan<E>>,
filterPredicate?: (E, number, $ReadOnlyArray<E>) => boolean,
},
arr: $ReadOnlyArray<E>
): Array<E> {
// no duplicated indices are allowed, ECMAScript Array.sort is not stable by spec
const indexSet = new Set(spans.map(([i]) => i));
if (indexSet.size !== spans.length) {
throw new Error(
"You cannot insert two spans at the same index. Combine the values of the spans."
);
let sortedSpans = [];
if (insertSpans !== undefined) {
// no duplicated indices are allowed, ECMAScript Array.sort is not stable by spec
const indexSet = new Set(insertSpans.map(([i]) => i));
if (indexSet.size !== insertSpans.length) {
throw new Error(
"You cannot insert two spans at the same index. Combine the values of the spans."
);
}
// sort spans by insertion position
sortedSpans = [...insertSpans];
sortedSpans.sort(([i], [j]) => i - j);
}
// sort spans by insertion position
const spansCopy = [...spans];
spansCopy.sort(([i], [j]) => i - j);
// The next span to insert
let nextSpanIndex = 0;
// build the new array in one pass
let ret = [];
let lastIndexInsertedAt = 0;
spansCopy.forEach(([index, contents]) => {
// All the content before this
ret = ret.concat(arr.slice(lastIndexInsertedAt, index));
for (let i = 0; i < arr.length; i += 1) {
if (nextSpanIndex < sortedSpans.length) {
const [index, contents] = sortedSpans[nextSpanIndex];
if (index === i) {
ret = ret.concat(contents);
nextSpanIndex += 1;
}
}
if (filterPredicate === undefined || filterPredicate(arr[i], i, arr)) {
ret.push(arr[i]);
}
}
// insert spans after the end of the array
for (let i = nextSpanIndex; i < sortedSpans.length; i += 1) {
const [_, contents] = sortedSpans[i];
ret = ret.concat(contents);
lastIndexInsertedAt = index;
});
ret = ret.concat(arr.slice(lastIndexInsertedAt));
}

@@ -63,2 +87,9 @@ return ret;

export function insertSpans<E>(
spans: $ReadOnlyArray<AddSpan<E>>,
arr: $ReadOnlyArray<E>
): Array<E> {
return modify({insertSpans: spans}, arr);
}
// Strict on length

@@ -65,0 +96,0 @@ export function zipWith<A, B, C>(

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc