updates to code

This commit is contained in:
Yanick Champoux 2023-01-10 12:30:40 -05:00
parent 2c3e73c48a
commit 04f22fb927
19 changed files with 420 additions and 124 deletions

21
src/filter.test.ts Normal file
View File

@ -0,0 +1,21 @@
import { expect, it } from "vitest";
import filter from "./filter.js";
import u from "./index.js";
it("can filter", () => {
const result = u({ foo: filter((v, k) => k === 1) })({
foo: ["a", "b"],
});
expect(result).to.eql({ foo: ["b"] });
});
it("freezes the result", () => {
expect(Object.isFrozen(filter([1], () => true))).to.be.true;
});
it("doesn't change the obj if nothing is modified", () => {
const orig = [1, 2, 3];
const result = filter(() => true)(orig);
expect(result).to.be.equal(orig);
});

12
src/filter.ts Normal file
View File

@ -0,0 +1,12 @@
import { filter as _filter } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => obj.length;
function filter(dataIn, predicate) {
const result = _filter.indexed(dataIn, predicate);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export default wrap(filter) as typeof _filter.indexed;

View File

@ -2,11 +2,23 @@ import freeze from "./freeze.js";
import is from "./is.js";
import _if from "./if.js";
import ifElse from "./ifElse.js";
import update, { omitted } from "./update.js";
import update, { skip } from "./update.js";
import updateIn from "./updateIn.js";
import constant from "./constant.js";
import omit from "./omit.js";
import omitBy from "./omitBy.js";
import pick from "./pick.js";
import pickBy from "./pickBy.js";
import filter from "./filter.js";
import reject from "./reject.js";
const functions = {
filter,
reject,
pickBy,
pick,
omit,
omitBy,
constant,
if: _if,
ifElse,
@ -14,11 +26,29 @@ const functions = {
freeze,
update,
updateIn,
omitted,
skip,
};
export {
filter,
reject,
pickBy,
pick,
omit,
omitBy,
constant,
_if as if,
ifElse,
is,
freeze,
update,
updateIn,
skip,
}
const merged = update;
Object.entries(functions).forEach(([k, v]) => (merged[k] = v));
export default merged as typeof merged & typeof functions;

View File

@ -1,58 +1,56 @@
import { expect, test, describe } from "vitest";
import map from "./map.js";
describe("map", () => {
test("applies updates to each item in an array", () => {
const object = [0, 1, 2];
const inc = (x) => x + 1;
const result = map(object, inc);
test("applies updates to each item in an array", () => {
const object = [0, 1, 2];
const inc = (x) => x + 1;
const result = map(object, inc);
expect(result).to.eql([1, 2, 3]);
});
expect(result).to.eql([1, 2, 3]);
});
test("applies updates to each value in an object", () => {
const object = { a: 0, b: 1, c: 2 };
const inc = (x) => x + 1;
const result = map(inc)(object);
test("applies updates to each value in an object", () => {
const object = { a: 0, b: 1, c: 2 };
const inc = (x) => x + 1;
const result = map(inc)(object);
expect(result).to.eql({ a: 1, b: 2, c: 3 });
});
expect(result).to.eql({ a: 1, b: 2, c: 3 });
});
test("can update with a regular updates object", () => {
const object = [{ a: 0 }, { a: 0 }];
const result = map({ a: 1 })(object);
test("can update with a regular updates object", () => {
const object = [{ a: 0 }, { a: 0 }];
const result = map({ a: 1 })(object);
expect(result).to.eql([{ a: 1 }, { a: 1 }]);
});
expect(result).to.eql([{ a: 1 }, { a: 1 }]);
});
test("returns the same object if no updates are made", () => {
const array = [0, 1];
const ident = (x) => x;
let result = map(ident)(array);
test("returns the same object if no updates are made", () => {
const array = [0, 1];
const ident = (x) => x;
let result = map(ident)(array);
expect(result).to.equal(array);
expect(result).to.equal(array);
const object = { a: 0 };
result = map(ident)(object);
const object = { a: 0 };
result = map(ident)(object);
expect(result).to.equal(object);
});
expect(result).to.equal(object);
});
test("passes the key or index as the second parameter to the iteratee", () => {
const object = {
a: { x: 0 },
b: [3, 3],
};
const setToKey = (_, key) => key;
const result = map(object, map(setToKey));
test("passes the key or index as the second parameter to the iteratee", () => {
const object = {
a: { x: 0 },
b: [3, 3],
};
const setToKey = (_, key) => key;
const result = map(object, map(setToKey));
expect(result).to.eql({
a: { x: "x" },
b: [0, 1],
});
});
test("freezes the result", () => {
expect(Object.isFrozen(map({}, {}))).to.be.true;
expect(result).to.eql({
a: { x: "x" },
b: [0, 1],
});
});
test("freezes the result", () => {
expect(Object.isFrozen(map({}, {}))).to.be.true;
});

11
src/matches.test.ts Normal file
View File

@ -0,0 +1,11 @@
import { test, expect } from "vitest";
import matches from "./matches.js";
test("basic", () => {
expect(matches(1, 1)).toBeTruthy();
expect(matches(1, 2)).not.toBeTruthy();
expect(matches({ a: 1, b: 2 }, { a: 1 })).toBeTruthy();
expect(matches({ a: 2, b: 2 }, { a: 1 })).not.toBeTruthy();
expect(matches({ 2: { a: (x) => x > 2 } })([1, 1, { a: 3 }])).toBeTruthy();
});

15
src/matches.ts Normal file
View File

@ -0,0 +1,15 @@
import { purry } from "remeda";
function matches(target, condition) {
if (typeof condition === "function") return condition(target);
if (typeof condition === "object") {
return Object.entries(condition).every(([key, value]) =>
matches(target[key], value)
);
}
return target === condition;
}
export default (...args) => purry(matches, args);

19
src/omit.test.ts Normal file
View File

@ -0,0 +1,19 @@
import { expect, it } from "vitest";
import omit from "./omit.js";
import u from "./index.js";
it("can omit a key", () => {
const result = u({ foo: omit(["bar"]) })({ foo: { bar: 7 } });
expect(result).to.eql({ foo: {} });
});
it("freezes the result", () => {
expect(Object.isFrozen(omit({}, ["a"]))).to.be.true;
});
it("doesn't change the obj if nothing is omitted", () => {
const orig = { a: 1 };
const result = omit(["b"])(orig);
expect(result).to.be.equal(orig);
});

16
src/omit.ts Normal file
View File

@ -0,0 +1,16 @@
import { omit as _omit } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => Object.keys(obj).length;
function omit(dataIn, toOmit) {
const result = _omit(dataIn, toOmit);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export interface Omit {
(dataIn: any, toOmit: (string | number)[]): any;
(toOmit: (string | number)[]): (dataIn: any) => any;
}
export default wrap(omit) as Omit;

20
src/omitBy.test.ts Normal file
View File

@ -0,0 +1,20 @@
import { expect, it, describe } from "vitest";
import u from "./index.js";
import omitBy from "./omitBy.js";
it("can omitBy with a function", () => {
const predicate = (value, key) => value === 7 && key === "bar";
const result = u({ foo: u.omitBy(predicate) })({ foo: { bar: 7, baz: "a" } });
expect(result).to.eql({ foo: { baz: "a" } });
});
it("freezes the result", () => {
expect(Object.isFrozen(u.omitBy("a" as any)({}))).to.be.true;
});
it("doesn't change the obj if nothing is omitted", () => {
const orig = { a: 1 };
const result = omitBy(() => false)(orig);
expect(result).to.be.equal(orig);
});

12
src/omitBy.ts Normal file
View File

@ -0,0 +1,12 @@
import { omitBy as _omitBy } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => Object.keys(obj).length;
function omitBy(dataIn, predicate) {
const result = _omitBy(dataIn, predicate);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export default wrap(omitBy) as typeof _omitBy;

23
src/pick.test.ts Normal file
View File

@ -0,0 +1,23 @@
import { expect, it } from "vitest";
import pick from "./pick.js";
import u from "./index.js";
it("can pick a key", () => {
const pickBar = pick(["bar"] as any);
const result = u({ foo: pickBar })({
foo: { bar: 7, baz: 8 },
});
expect(result).to.eql({ foo: { bar: 7 } });
});
it("freezes the result", () => {
expect(Object.isFrozen(pick({ a: 1 }, ["a"]))).to.be.true;
});
it("doesn't change the obj if nothing is modified", () => {
const orig = { a: 1 };
const result = pick<any, string>(["a"])(orig);
expect(result).to.be.equal(orig);
});

12
src/pick.ts Normal file
View File

@ -0,0 +1,12 @@
import { pick as _pick } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => Object.keys(obj).length;
function pick(dataIn, keys) {
const result = _pick(dataIn, keys);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export default wrap(pick) as typeof _pick;

21
src/pickBy.test.ts Normal file
View File

@ -0,0 +1,21 @@
import { expect, it } from "vitest";
import pickBy from "./pickBy.js";
import u from "./index.js";
it("can pick a key", () => {
const result = u({ foo: pickBy((v, k) => k === "bar") })({
foo: { bar: 7, baz: 8 },
});
expect(result).to.eql({ foo: { bar: 7 } });
});
it("freezes the result", () => {
expect(Object.isFrozen(pickBy({}, () => true))).to.be.true;
});
it("doesn't change the obj if nothing is modified", () => {
const orig = { a: 1 };
const result = pickBy(() => true)(orig);
expect(result).to.be.equal(orig);
});

12
src/pickBy.ts Normal file
View File

@ -0,0 +1,12 @@
import { pickBy as _pick } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => Object.keys(obj).length;
function pickBy(dataIn, predicate) {
const result = _pick(dataIn, predicate);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export default wrap(pickBy) as typeof _pick;

21
src/reject.test.ts Normal file
View File

@ -0,0 +1,21 @@
import { expect, it } from "vitest";
import reject from "./reject.js";
import u from "./index.js";
it("can reject", () => {
const result = u({ foo: reject((v, k) => k === 1) })({
foo: ["a", "b"],
});
expect(result).to.eql({ foo: ["a"] });
});
it("freezes the result", () => {
expect(Object.isFrozen(reject([1], () => true))).to.be.true;
});
it("doesn't change the obj if nothing is modified", () => {
const orig = [1, 2, 3];
const result = reject(() => false)(orig);
expect(result).to.be.equal(orig);
});

12
src/reject.ts Normal file
View File

@ -0,0 +1,12 @@
import { reject as _reject } from "remeda";
import wrap from "./wrap.js";
const sizeOf = (obj) => obj.length;
function reject(dataIn, predicate) {
const result = _reject.indexed(dataIn, predicate);
return sizeOf(result) === sizeOf(dataIn) ? dataIn : result;
}
export default wrap(reject) as typeof _reject.indexed;

View File

@ -0,0 +1,23 @@
import { test, expect, describe } from "vitest";
import u from "./index.js";
describe("update", () => {
test("basic", () => {
const orig = { a: 1 };
const result = u(orig, { a: 1 });
expect(result).toBe(orig);
});
test("array", () => {
const orig = [1, 2, 3];
const result = u(orig, { 1: 2 });
expect(result).toBe(orig);
});
test.only("with u.skip", () => {
const orig = { a: 1 };
const result = u(orig, { b: u.skip });
expect(result).toBe(orig);
});
});

View File

@ -1,10 +1,10 @@
import wrap from "./wrap.js";
import constant from "./constant.js";
import { omitBy, isObject } from "remeda";
import { omitBy, isObject, merge } from "remeda";
const innerOmitted = { __omitted: true };
export const omitted = constant(innerOmitted);
const innerOmitted = { __skip: true };
export const skip = constant(innerOmitted);
function isEmpty(object) {
return !Object.keys(object).length;
@ -86,6 +86,12 @@ function update(object, updates) {
const resolvedUpdates = resolveUpdates(updates, defaultedObject);
Object.entries(resolvedUpdates).forEach(([key, value]) => {
if (value === innerOmitted) {
if (!defaultedObject.hasOwnProperty(key)) delete resolvedUpdates[key];
}
});
if (isEmpty(resolvedUpdates)) {
return defaultedObject;
}
@ -97,7 +103,7 @@ function update(object, updates) {
}
return omitBy(
{ ...defaultedObject, ...resolvedUpdates },
merge(defaultedObject, resolvedUpdates),
(value) => value === innerOmitted
);
}

View File

@ -2,154 +2,166 @@ import { expect, it, describe } from "vitest";
import u from "./index.js";
it("does not change anything if no updates are specified", () => {
const object = { foo: 3, bar: [7, 5] };
const result = u(object, {});
const object = { foo: 3, bar: [7, 5] };
const result = u(object, {});
expect(result).to.equal(object);
expect(result).to.equal(object);
});
it("can update with fixed values", () => {
const object = { foo: 3, bar: [7, 5] };
const result = u(object, { foo: 4 });
const object = { foo: 3, bar: [7, 5] };
const result = u(object, { foo: 4 });
expect(result).to.deep.equal({ foo: 4, bar: [7, 5] });
expect(result).to.deep.equal({ foo: 4, bar: [7, 5] });
});
it("returns the same instance if an update doesn't make changes", () => {
const object = { foo: 3 };
const result = u({ foo: 3 })(object);
const object = { foo: 3 };
const result = u({ foo: 3 })(object);
expect(result).to.equal(object);
expect(result).to.equal(object);
});
it("can update a nested structure", () => {
const object = { foo: { bar: 7, bam: 3 }, baz: 32 };
const result = u({ foo: { bar: 8 } })(object);
const object = { foo: { bar: 7, bam: 3 }, baz: 32 };
const result = u({ foo: { bar: 8 } })(object);
expect(result).to.deep.equal({ foo: { bar: 8, bam: 3 }, baz: 32 });
expect(result).to.deep.equal({ foo: { bar: 8, bam: 3 }, baz: 32 });
});
it("can update arrays", () => {
const object = [1, 2, 3];
const result = u({ 1: 7 })(object);
const object = [1, 2, 3];
const result = u({ 1: 7 })(object);
expect(result).to.deep.equal([1, 7, 3]);
expect(result).to.deep.equal([1, 7, 3]);
});
it("replaces the object outright if updates are a constant", () => {
expect(u(3)({})).to.equal(3);
expect(u(null)({})).to.be.null;
expect(u(3)({})).to.equal(3);
expect(u(null)({})).to.be.null;
});
it("can add an element to an array", () => {
const object = [];
const result = u({ 0: 3 })(object);
const object = [];
const result = u({ 0: 3 })(object);
expect(result).to.eql([3]);
expect(result).to.eql([3]);
});
it("can update nested arrays", () => {
const object = { foo: [1, 2, 3], bar: 9 };
const result = u({ foo: { 1: 7 } })(object);
const object = { foo: [1, 2, 3], bar: 9 };
const result = u({ foo: { 1: 7 } })(object);
expect(result).to.deep.equal({ foo: [1, 7, 3], bar: 9 });
expect(result).to.deep.equal({ foo: [1, 7, 3], bar: 9 });
});
it("can use functions to update values", () => {
const inc = (i) => i + 1;
const object = { foo: 3, bar: 4, baz: 7 };
const result = u({ foo: inc, bar: inc })(object);
const inc = (i) => i + 1;
const object = { foo: 3, bar: 4, baz: 7 };
const result = u({ foo: inc, bar: inc })(object);
expect(result).to.deep.equal({ foo: 4, bar: 5, baz: 7 });
expect(result).to.deep.equal({ foo: 4, bar: 5, baz: 7 });
});
it("can be partially applied", () => {
const inc = (i) => i + 1;
const object = { foo: 3 };
const incFoo = u({ foo: inc });
const inc = (i) => i + 1;
const object = { foo: 3 };
const incFoo = u({ foo: inc });
const result = incFoo(object);
const result = incFoo(object);
expect(result).to.deep.equal({ foo: 4 });
expect(result).to.deep.equal({ foo: 4 });
});
it("can update if the value is an array", () => {
const object = {};
const result = u({ foo: [0, 1] })(object);
const object = {};
const result = u({ foo: [0, 1] })(object);
expect(result).to.deep.equal({ foo: [0, 1] });
expect(result).to.deep.equal({ foo: [0, 1] });
});
it("can update when original object is undefined", () => {
const result = u({ foo: [0, 1] })(undefined);
const result = u({ foo: [0, 1] })(undefined);
expect(result).to.deep.equal({ foo: [0, 1] });
expect(result).to.deep.equal({ foo: [0, 1] });
});
it("can take a function as the updater", () => {
const result = u((i) => i + 1)(7);
const result = u((i) => i + 1)(7);
expect(result).to.eql(8);
expect(result).to.eql(8);
});
it("deeply freezes the result", () => {
const result = u({ foo: { bar: 3 } }, { foo: { bar: 0 } });
const result = u({ foo: { bar: 3 } }, { foo: { bar: 0 } });
expect(Object.isFrozen(result)).to.be.true;
expect(Object.isFrozen(result.foo)).to.be.true;
expect(Object.isFrozen(result)).to.be.true;
expect(Object.isFrozen(result.foo)).to.be.true;
});
it("assigns null values", () => {
expect(u({ isNull: null }, {})).to.eql({ isNull: null });
expect(u({ isNull: null }, {})).to.eql({ isNull: null });
});
it("defaults to an empty object when null or undefined", () => {
let result = u({ a: { b: 0 } })({ a: null });
expect(result).to.eql({ a: { b: 0 } });
let result = u({ a: { b: 0 } })({ a: null });
expect(result).to.eql({ a: { b: 0 } });
result = u({ a: { b: 0 } })({ a: undefined });
expect(result).to.eql({ a: { b: 0 } });
result = u({ a: { b: 0 } })({ a: undefined });
expect(result).to.eql({ a: { b: 0 } });
result = u({ a: { b: 0 } })({});
expect(result).to.eql({ a: { b: 0 } });
result = u({ a: { b: 0 } })({});
expect(result).to.eql({ a: { b: 0 } });
});
it("preserves empty objects when empty updates are specified", () => {
const result = u({ a: {} })({});
expect(result).to.eql({ a: {} });
const result = u({ a: {} })({});
expect(result).to.eql({ a: {} });
});
it("works with date objects", () => {
const date = new Date();
const result = u({ created: date })({});
expect(result).toEqual({ created: date });
const date = new Date();
const result = u({ created: date })({});
expect(result).toEqual({ created: date });
});
const expectU = (update, orig, expected) =>
expect(update(orig)).to.eql(expected);
expect(update(orig)).to.eql(expected);
describe("u.omitted", () => {
it("omit properties via u.omitted", () => {
expectU(u({ a: u.omitted, b: (i) => i + 1 }), { a: 1, b: 2 }, { b: 3 });
});
describe("u.skip", () => {
it("omit properties via u.skip", () => {
expectU(u({ a: u.skip, b: (i) => i + 1 }), { a: 1, b: 2 }, { b: 3 });
});
it("omit array and object properties", () => {
expectU(
u({ a: u.omitted, b: "stuff", c: u.omitted }),
{ a: [1, 2, 3], b: "orig", c: { z: "bar" } },
{ b: "stuff" }
);
});
it("omit array and object properties", () => {
expectU(
u({ a: u.skip, b: "stuff", c: u.skip }),
{ a: [1, 2, 3], b: "orig", c: { z: "bar" } },
{ b: "stuff" }
);
});
it("deep omit", () => {
expectU(
u({ a: { b: u.omitted, c: "stuff" } }),
{ a: { b: "foo", z: "bar" } },
{ a: { z: "bar", c: "stuff" } }
);
});
it("deep omit", () => {
expectU(
u({ a: { b: u.skip, c: "stuff" } }),
{ a: { b: "foo", z: "bar" } },
{ a: { z: "bar", c: "stuff" } }
);
});
it("omitting an array item filters it out", () => {
expectU(u({ 1: u.omitted }), ["a", "b", "c"], ["a", "c"]);
});
it("omitting an array item filters it out", () => {
expectU(u({ 1: u.skip }), ["a", "b", "c"], ["a", "c"]);
});
it("doesn't change the obj if nothing is omitted", () => {
const orig = { a: 1 }
const result = u(orig, { b: u.skip })
expect(result).to.be.equal(orig)
})
it("doesn't change the array if nothing is omitted", () => {
const orig = [1, 2, 3]
const result = u({ 4: u.skip })(orig)
expect(result).to.be.equal(orig)
})
});