// Import the module
import ArrayContract from "[location of ArrayByContractFactory.js]";
// create a contracted Array constructor
const ArrayCTOR = ArrayContract({
contract, /*:function => mandatory*/
named, /*:string|undefined => creates a named constructor*/,
customInfo, /*:string|undefined => used to display info ([instance].info)*/,
maxLen, /*number|undefined => maximum length*/,
flatten, /*boolean|undefined => Array is always unidimensional*/
}) /*:function (constructor)*/;
// contract syntax
function(value /*:any*/) { /*...*/ } /*:any|undefined */
// instantiate
const myArrayThing = ArrayCTOR(...values /*:...<any>*/) /*:Object (ArrayCTOR instance)*/
// Create a constructor to wrap Arrays of integers.
// Note: factory imported as ArrayContract
const IntArray = ArrayContract({
named: "IntArray",
// ^ [instance].constructor will be function IntArray() {...}
customInfo: "Can only contain integers (may contain nested Arrays).",
// ^ customInfo is used for [instance].info
contract: v => Number.isInteger(+v) ? +v : undefined
// ^ Number strings (e.g. "42") are converted to Number
});
// create IntArray instance
const myIntArray = IntArray(1, 3, 4, 5, 5.5, 2, `six`, `seven`, 2, 1);
// Note: nested array values are recursively included
let sum = (a, v) => a + (Array.isArray(v) ? v.reduce(sum) : v);
const myIntArraySum = myIntArray.reduce(sum, 0);
const myMappedIntArraySum = myMappedIntArray.reduce(sum, 0);
// if one want to factor out recursion in the reducer:
let sumFlat = (a, v) => a + v;
const myMappedIntArraySumFlat = myMappedIntArray.flat().reduce(sumFlat, 0);
// Create constructor for Arrays of non empty strings
const nonEmptyStringArr = ArrayContract({
contract: v => typeof v === "string" && v.trim().length > 0 ? v : undefined
});
// create nonEmptyStringArr instance
const myStringArray = nonEmptyStringArr("hithere", 42);
// A reducer for nonEmptyStringArr instances
// Note: this reducer delivers an Array. That will automagically
// be converted to a new nonEmptyStringArr instance.
// Note: nested Arrays are recursively included in the reducer
const strReducer = (acc, str) => {
switch(true) {
case Array.isArray(str): return str.reduce(strReducer, [acc.at(0)]);
default: return str === acc[0] ? acc : [...acc, `${acc[0]} ${str}`];
}
};
// create a new instance with the reducer
const myReducedStringArray = myStringArray
.reduce( strReducer, [myStringArray.at(0)]);
// create contructor for Arrays of {a, b, c}
// Note: the [flatten] parameter will accomplish that
// the the wrapped Array will be uni-dimensional
const specificObjects = ArrayContract({
contract: input => {
return Object.keys(input || []).join("") === "abc" &&
Object.values(input || {}).every(v =>
typeof v === "number" && !Number.isNaN(v) && v !== Infinity)
? input : undefined;
},
flatten: true,
});
// create specificObjects instance
const myObjs = specificObjects(
{a: null, b: 1, c: 2}, /* not accepted */
"abc", /* not accepted */
42, /* not accepted */
{a: 40, b: 41, c: 42, d: 42}, /* not accepted */
[{a: 42, b: 41, c: 40}, 42], /* single value 42 removed */
{a: 40.5, b: 41.5, c: 42.0},
{a: 0, b: 1, c: 2}
);
// values can also be set as in native Arrays
myObjs[42] = {a: 0.1, b: 0.2, c: 0.3};
// ^ will become automagically the last entry
// (an instance will never become sparse)
myObjs[0] = Object.fromEntries([["a" ,1], ["b", 3] , ["c", 5]]);
myObjs[0] = "no can do"; // myObjs[0] will keep its original value
// Note: nested Arrays are recursively included in the mapper
const addFY = addValue =>
v => Array.isArray(v) ? v.map(addFY(addValue)) : v + addValue;
const myMappedIntArray = myIntArray.map(addFY(-25));
// note: .slice delivers a clone.
// .forEach does not touch the encapsulated Array
const myStrings = myStringArray.slice(1).flat();
const myStringsChanged = nonEmptyStringArr();
myStrings.forEach(str => myStringsChanged.push(`HELLO ${str}`));
// an instance is iterable, so this works
const myStringsAsArray = [...myStringArray].flat().slice(1);
const myStringsAsNewInstance = nonEmptyStringArr(...myStringsAsArray);