Skip to content

Instantly share code, notes, and snippets.

@Muzietto
Last active April 3, 2025 06:07
Show Gist options
  • Save Muzietto/336141ccf57469bcd652c77df13a625c to your computer and use it in GitHub Desktop.
Save Muzietto/336141ccf57469bcd652c77df13a625c to your computer and use it in GitHub Desktop.
Recur or Perish
// EXPERIMENTAL STUFF (not working) - tests are failing
// truly recursive drill down for arrays
export const dda = pos => ctx => visitor => {
console.log('pos', pos);
console.log('ctx', ctx);
console.log('visitor', visitor);
return [
...ctx.slice(0, pos),
visitor(),
...ctx.slice(pos + 1)
];
}
// WORKING STUFF - tests are ok
// drilldown in array
export function ddArray(ctx, pos) {
// console.log('aaa2', ctx, pos);
return visitor => [
...ctx.slice(0, pos),
visitor(),
...ctx.slice(pos + 1)
];
}
// drilldown in object
export function ddObject(ctx, key) {
return (visitor, swapKey) => {
// console.log('swapKey=', !!swapKey);
return !swapKey
? {
...ctx,
[key]: visitor(),
}
: Object.keys(ctx).reduce(
(acc, curr) =>
curr !== key
? { ...acc, [curr]: ctx[curr] }
: { ...acc, [visitor()]: ctx[key] },
{},
);
};
}
// returns an array without an element or an object without a keyvalue
export function removed(ctx) { // ctx = valore, see JSObject/JSArray - remover={r => returner(() => removed(valore)(r))}
return keyF => { // see Transformed - const newCurrentValue = remover(() => chiave);
const key = keyF();
if (ctx.length && ctx.slice) {
console.log('removing from array');
return [...ctx.slice(0, key), ...ctx.slice(key + 1)];
}
console.log('removing from object');
return Object.keys(ctx).reduce(
(acc, curr) =>
(curr !== key)
? { ...acc, [curr]: ctx[curr] }
: acc,
{},
);
}
}
// returns an array with an added element or an object with an added keyvalue
export function added(ctx) { // ctx = valore, see JSObject/JSArray - adder={r => returner(() => added(valore)(r))}
return keyF => { // see Transformed - const newCurrentValue = adder(() => thingToAdd);
const thingToAdd = keyF();
console.log('added - ctx is', ctx)
if (ctx.length && ctx.slice) {
console.log('adding to array', thingToAdd);
return [...ctx, thingToAdd]; // always at the end
}
console.log('adding to object', thingToAdd);
return {
...ctx,
...thingToAdd, // gotta ensure it's an object
};
}
}
// utils stuff
export function isObject(valore) {
return (typeof valore === 'object') && !Array.isArray(valore);
}
export function isArray(valore) {
return Array.isArray(valore);
}
export function isBoolean(valore) {
return (typeof valore === 'boolean');
}
export function isString(valore) {
return typeof valore === 'string';
}
export function isNumber(valore) {
return typeof valore === 'number';
}
export function whatTypeIs(valore) {
if (isObject(valore)) return 'object';
if (isArray(valore)) return 'array';
if (isBoolean(valore)) return 'boolean';
if (isString(valore)) return 'string';
if (isNumber(valore)) return 'number';
}
export function isArrayIndex(chiave) {
return [...Array(101).keys()]
.map(c => c + '')
.includes(chiave + '');
}
import {
ddArray,
ddObject,
added,
removed,
dda,
} from './traverser';
describe('in the implemented world', () => {
describe('ddArray drills down an array', () => {
it('very shallow', () => {
const returner0 = r => ddArray(['a', 'b'], 0)(r);
const result0 = returner0(() => 'ciao');
expect(result0).toEqual(['ciao', 'b']);
const returner1 = r => ddArray(['a', 'b'], 1)(r);
const result1 = returner1(() => 'ciao');
expect(result1).toEqual(['a', 'ciao']);
});
it('a little bit deeper', () => {
const returner1 = r => ddArray(['a', 'b', [0, 1]], 2)(r);
const returner2 = r => returner1(() => ddArray([0, 1], 0)(r)); // this is wasteful - we shouldn't have to express [0,1] ourselves !!!
const result = returner2(() => 'ciao');
expect(result).toEqual(['a', 'b', ['ciao', 1]]);
});
it('a lot deeper', () => {
const returner1 = r => ddArray(['a', 'b', [0, [1]]], 2)(r);
const returner2 = r => returner1(() => ddArray([0, [1]], 1)(r)); // this is wasteful
const returner3 = r => returner2(() => ddArray([1], 0)(r)); // this is wasteful
const result = returner3(() => 'ciao');
expect(result).toEqual(['a', 'b', [0, ['ciao']]]);
});
});
describe('ddObject drills down an object', () => {
it('very shallow', () => {
const returner0 = r => ddObject({ qwe: 'a', wer: 'b' }, 'qwe')(r);
const result0 = returner0(() => 'ciao');
expect(result0).toEqual({ qwe: 'ciao', wer: 'b' });
const returner1 = r => ddObject({ qwe: 'a', wer: 'b' }, 'wer')(r);
const result1 = returner1(() => 'ciao');
expect(result1).toEqual({ wer: 'ciao', qwe: 'a' });
});
it('a little bit deeper', () => {
const returner1 = r => ddObject({ qwe: 'a', wer: { asd: 'b' } }, 'wer')(r);
const returner2 = r => returner1(() => ddObject({ asd: 'b' }, 'asd')(r)); // this is wasteful
const result = returner2(() => 'ciao');
expect(result).toEqual({ qwe: 'a', wer: { asd: 'ciao' } });
});
it('a lot deeper', () => {
const returner1 = r => ddObject({ qwe: 'a', wer: { asd: { tyu: 'b' } } }, 'wer')(r);
const returner2 = r => returner1(() => ddObject({ asd: { tyu: 'b' } }, 'asd')(r)); // this is wasteful
const returner3 = r => returner2(() => ddObject({ tyu: 'b' }, 'tyu')(r)); // this is wasteful
const result = returner3(() => 'ciao');
expect(result).toEqual({ qwe: 'a', wer: { asd: { tyu: 'ciao' } } });
});
});
describe('ddArray and ddObject work together', () => {
it('in a simple example', () => {
const returner0 = r => ddObject({ qwe: 'a', wer: ['b', 'c'] }, 'wer')(r);
const returner1 = r => returner0(() => ddArray(['b', 'c'], 0)(r)); // this is wasteful
const result1 = returner1(() => 'ciao');
expect(result1).toEqual({ qwe: 'a', wer: ['ciao', 'c'] });
});
it('in a complex example', () => {
const returner0 = r => ddObject({ qwe: 'a', wer: ['b', { xcv: 123, ert: true }] }, 'wer')(r);
const returner1 = r => returner0(() => ddArray(['b', { xcv: 123, ert: true }], 1)(r)); // this is wasteful
const returner2 = r => returner1(() => ddObject({ xcv: 123, ert: true }, 'ert')(r)); // this is wasteful
const result1 = returner2(() => 'ciao');
expect(result1).toEqual({ qwe: 'a', wer: ['b', { xcv: 123, ert: 'ciao' }] });
});
});
describe('removed returns an array without an element or an object without a keyvalue', () => {
it('in a very shallow situation', () => {
const remover0 = () => removed(['a', 'b'])
const result0 = remover0()(() => 0);
expect(result0).toEqual(['b']);
const remover1 = () => removed({ qwe: 123, ert: 345 })
const result1 = remover1()(() => 'ert');
expect(result1).toEqual({ qwe: 123 });
});
it('in a less shallow situation', () => {
const returner0 = r => ddArray(['a', { qwe: 123, ert: 345 }], 1)(r);
const remover1 = r => returner0(() => removed({ qwe: 123, ert: 345 })(r)); // wasteful
const result1 = remover1(() => 'qwe');
expect(result1).toEqual(['a', { ert: 345 }]);
});
it('in a deep situation', () => {
const returner0 = r => ddArray(['a', { qwe: 123, ert: [234, true] }], 1)(r);
const returner1 = r => returner0(() => ddObject({ qwe: 123, ert: [234, true] }, 'ert')(r)); // this is wasteful
const remover2 = r => returner1(() => removed([234, true])(r)); // wasteful
const result2 = remover2(() => 1);
expect(result2).toEqual(['a', { qwe: 123, ert: [234] }]);
const returner00 = r => ddArray(['a', { qwe: 123, ert: [234, true] }], 1)(r);
const remover01 = r => returner00(() => removed({ qwe: 123, ert: [234, true] })(r)); // wasteful
const result21 = remover01(() => 'ert');
expect(result21).toEqual(['a', { qwe: 123 }]);
});
});
describe('added returns an array with an added element or an object with an added keyvalue', () => {
it('in a very shallow situation', () => {
const adder0 = () => added(['a', 'b'])
const result0 = adder0()(() => 'c');
expect(result0).toEqual(['a', 'b', 'c']);
const adder1 = added({ qwe: 123, ert: 345 })
const result1 = adder1(() => ({ rty: 567 }));
expect(result1).toEqual({ qwe: 123, ert: 345, rty: 567 });
});
it('in a less shallow situation', () => {
const returner0 = r => ddArray(['a', { qwe: 123, ert: 345 }], 1)(r);
const adder1 = r => returner0(() => added({ qwe: 123, ert: 345 })(r)); // wasteful
const result1 = adder1(() => ({ rty: 567 }));
expect(result1).toEqual(['a', { qwe: 123, ert: 345, rty: 567 }]);
});
it('in a deep situation', () => {
const returner0 = r => ddArray(['a', { qwe: 123, ert: [234, true] }], 1)(r);
const returner1 = r => returner0(() => ddObject({ qwe: 123, ert: [234, true] }, 'ert')(r)); // this is wasteful
const adder2 = r => returner1(() => added([234, true])(r)); // wasteful
const result2 = adder2(() => 1);
expect(result2).toEqual(['a', { qwe: 123, ert: [234, true, 1] }]);
const returner00 = r => ddArray(['a', { qwe: 123, ert: [234, true] }], 1)(r);
const adder01 = r => returner00(() => added({ qwe: 123, ert: [234, true] })(r)); // wasteful
const result21 = adder01(() => ({ rty: false }));
expect(result21).toEqual(['a', { qwe: 123, ert: [234, true], rty: false }]);
});
});
});
xdescribe('in the still experimental world', () => {
describe('dda drills down an array with REALLY recursively built context', () => {
xit('very shallow', () => { // this test works, but it is too simple to express a true drilldown
const returner0 = dda(0)(['a', 'b']);
const result0 = returner0(() => 'ciao');
expect(result0).toEqual(['ciao', 'b']);
});
it('a little bit deeper', () => {
// const contexter1 = dda(2)(['a', 'b', [0, 1]]); // visitor => result
// const returner2 = v => dda(0)(contexter1(v));
const returner1 = dda(2)(['a', 'b', [0, 1]]); // visitor => result
const returner2 = r => returner1(() => dda(0)(c)(r));
const result = returner2(() => 'ciao');
expect(result).toEqual(['a', 'b', ['ciao', 1]]);
});
});
});
@Muzietto
Copy link
Author

Muzietto commented Apr 3, 2025

Inside the traverser.js library, methods ddArray and ddObject are wasteful, because they require to express the context in which they operate at each recursion level. They are working anyhow, and their tests are described with "in the implemented world" in the specs file.

Specs file shows several usages of them in order to:

  • drill down arrays and objects + swap values
  • drill down and remove values, assisted by method "removed"
  • drill down and add values, assisted by method "added"

Method dda is an attempt to create a recursion that drills inside an array without any additional help, but it's not working. Related tests are described with "in the still experimental world".

the scope of this gist is to prepare:

  • a dda method that really drills recursively inside arrays
  • a future ddo method that really drills recursively inside objects
  • a future added2 method that operates together with dda/ddo
  • a future removed2 method that operates together with dda/ddo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment