Memoize accepting Symbol('foo') as an argument - javascript

I am trying to implement Memoize function that takes Symbol() as an argument. Is there a way to add Symbol('foo') as an argument?
let obj = {};
let counter = 1;
function foo() {
counter += 1;
return counter;
}
function memoize(fn) {
const cache = {};
return (...args) => {
const stringifiedArgs = JSON.stringify(args);
const result = (cache[stringifiedArgs] = !cache.hasOwnProperty(
stringifiedArgs
)
? fn(...args)
: cache[stringifiedArgs]);
return result;
};
}
let id = Symbol('id');
const memoizedFoo = memoize(foo);
console.log(memoizedFoo(id)); // 2
console.log(memoizedFoo(null)); // 2
console.log(memoizedFoo(id)); // 2
console.log(memoizedFoo(null)); //2
console.log(memoizedFoo(5)); // 3
console.log(memoizedFoo(5)); // 3
console.log(memoizedFoo(obj)); // 4
console.log(memoizedFoo(obj)); // 4
console.log(memoizedFoo(4)); // 5

The reason it does not work is that a symbol has no JSON equivalent, and so JSON.stringify will return null for any Symbols in the arguments array.
One way to solve it is to apply toString to it:
const stringifiedArgs = JSON.stringify(
args.map(arg => typeof arg === "symbol" ? arg.toString() : arg)
);
You could of course extend this to other types, or else use a Map which supports any type as its keys.

Related

How I can reset variable value when function end?

I have such function and global variable (as array):
const arraysList = []
export const changeColorCategories = (array, draggedColumnId) => {
const isColor = arraysList.length ? arraysList[0][0]?.color : [];
if (typeof isColor === 'string') {
firstLevelColor = isColor;
}
return array.map((item, index, categories) => {
item.color = draggedColumnId !== 3 ? '#010172' : '#000000';
arraysList.push(categories);
if (firstLevelColor && !draggedColumnId) {
item.color = firstLevelColor;
}
if (item?.children?.length) {
changeColorCategories(item.children);
}
return item;
})
}
Every call of this function push some data to array. In this function I use recursion. So how i can clear this array only when this function will end it's work.
You can call the recursion function inside another function this way you can run anything you want when the function ends
const arraysList = []
export const changeColorCategories = (array, draggedColumnId) => {
const isColor = arraysList.length ? arraysList[0][0]?.color : [];
if (typeof isColor === 'string') {
firstLevelColor = isColor;
}
return array.map((item, index, categories) => {
item.color = draggedColumnId !== 3 ? '#010172' : '#000000';
arraysList.push(categories);
if (firstLevelColor && !draggedColumnId) {
item.color = firstLevelColor;
}
if (item?.children?.length) {
changeColorCategories(item.children);
}
return item;
})
}
function runRucFunc(){
const result = changeColorCategories();
//Your other code goes here
return result;
}
You can just put your recursion part inside a sub function.
Below I've called the inner function inner, I've also moved the arrayList into the function, due to closures you wound't even need to clear the arrayList, it would be cleared automatically as it goes out of scope.
eg.
export const changeColorCategories = (array, draggedColumnId) => {
const arraysList = []
function inner(array, draggedColumnId) {
const isColor = arraysList.length ? arraysList[0][0]?.color : [];
if (typeof isColor === 'string') {
firstLevelColor = isColor;
}
return array.map((item, index, categories) => {
item.color = draggedColumnId !== 3 ? '#010172' : '#000000';
arraysList.push(categories);
if (firstLevelColor && !draggedColumnId) {
item.color = firstLevelColor;
}
if (item?.children?.length) {
inner(item.children); //we call inner here instead.
}
return item;
})
}
// now call our inner
// you could do something even before your recursion.
const result = inner(array, draggedColumnId);
// here we can put what we want after recursion.
return result;
}
You could wrap the recursive call in another function like so:
const arr = []
const recursive = (counter = 0) => {
if(counter === 5)
return arr.map((v) => String.fromCodePoint(65 + v))
arr.push(counter)
return recursive(++counter)
}
const go = () => {
console.log(recursive()) // [A,B,C,D,E]
console.log(arr) // [0,1,2,3,4]
arr.length = 0 // clear the array
console.log(arr) // []
}
go()
Alternatively, if the global array does not actually need to be global, and is merely a container for working information of the recursive algorithm, then you could make it a parameter of the recursive function, which will then fall out of scope (and be garbage collected) when the recursion ends.
const recursive = (counter = 0, arr = []) => {
if(counter === 5)
return arr.map((v) => String.fromCodePoint(65 + v))
arr.push(counter)
return recursive(++counter, arr)
}
console.log(recursive()) // [A,B,C,D,E]
console.log(arr) // Error! Not in scope!
go()
Or, you could make the recursive function more intelligent and able to detect when it is processing the final recursion: how this is done will depend on the precise logic of the recursive function.

Assign anonymous injected method factory name of caller

I need the name function to be defined for use with another method further down so I need it to be dynamically named.
currently...
`use strict`;
const factory = () => () => {}
const method = factory();
method.name === undefined
&&
const factory = () => { const name = () => {}; return name }
const method = factory();
method.name === "name"
because...
`use strict`;
const factory = () => () => {}
factory() === () => {} // and () => {} is not a named method
But I want...
`use strict`;
const factory = () => () => {}
const method = factory();
method.name === "method"
this is extra description because stack overflow wants me to say more but I think that the problem is self-explanatory
Make your arrow function a traditional function:
const factory = () => function method() {}
If the name is to be dynamic, then we have to deal with the fact that a function's name property is read-only. The function definition could happen in a dynamic way (but that involves parsing), or we could create a proxy:
const factory = () => new Proxy(() => console.log("hello"), {
get(obj, prop) {
return prop === "name" ? "method" : prop;
}
});
const method = factory();
console.log(method.name);
method();
Variable name
In comments you explained that the dynamic name should be determined by the variable name (i.e. method), but:
There can be many variables referring to the same function, while a function has only one name -- so that leads to an unresolvable paradox.
Variable names only exist at parse time, not at execution time.
You can do that by creating an object with the dynamic name prop and assigning the function to that prop:
`use strict`;
const factory = s => (
{[s]: () => {}}[s]
)
const method = factory('blah');
console.log(method.name)
Does this work? You'd have to repeat the name, though, so not sure it totally gets what you're looking for
let factory = (name) => new Function(`return function ${name}() {}`)();
let method = factory("method");
method.name; // "method"
Building off of #trincot's answer, what was initially proposed is not possible.
However, here's what I went with for a sufficient alternative:
`use strict`;
// #trincot's answer
const renameFunction = (name, func) => new Proxy(func, {
get(obj, prop) {
return prop === "name" ? name : prop;
}
})
const identifyFunction = (obj) => {
if (typeof obj === "object") {
// because the object has the original defined as a property
// we can pull the name back out
const name = Object.keys(obj)[0];
const func = obj[name];
return renameFunction(name, func)
}
// if it's not an object then assume it's named and return
return obj
}
const useFooName = (foo) => {
let _foo = foo;
// only need to identify if it's an object
if (typeof _foo === "object") {
_foo = identifyFunction(foo)
}
// using both the function and its name
console.log(`${_foo.name} ${_foo()}`);
}
const factory = (func) => func;
const foo = factory(() => "bar");
useFooName(foo) // bar
useFooName({ foo }) // foo bar
This can be used dynamically as well...
`use strict`;
// #trincot's answer
const renameFunction = (name, func) => new Proxy(func, {
get(obj, prop) {
return prop === "name" ? name : prop;
}
})
const identifyFunction = (obj) => {
if (typeof obj === "object") {
// because the object has the original defined as a property
// we can pull the name back out
const name = Object.keys(obj)[0];
const func = obj[name];
return renameFunction(name, func)
}
// if it's not an object then assume it's named and return
return obj
}
// let there be a function that accepts and function bar
// and uses bar's name; for whatever reason you don't want to
// change this method
const useBarName = (bar) => `${bar.name} ${bar()}`
// let there also be an arbitrary amount of functions using this
const traditionalWay = () => {
const a = () => "b";
const c = () => "d";
const e = () => "f";
console.log([a, c, e].map(useBarName).join(" ")); // a b c d e f ...
}
// but instead of hard-coding the functions it'd be easier
// to just build a factory that did it automatically
const brokenWay = () => {
const factory = (letter) => () => letter;
const a = factory("b");
const c = factory("d");
const e = factory("f");
console.log([a, c, e].map(useBarName).join(" ")); // b d f ...
}
// so instead you can use the indentifyFunction to identify
// the anonymous methods
const newWay = () => {
const factory = (letter) => () => letter;
const a = factory("b");
const c = factory("d");
const e = factory("f");
console.log([{a}, {c}, {e}].map(identifyFunction).map(useBarName).join(" ")); // a b c d e f ...
}
traditionalWay();
brokenWay();
newWay();

returning object that returns functions

I need to write a function in JavaScript that takes a number and returns an object that returns chainable functions (without using OOP).
Example:
func(3).not().not().equals(4)
would outputs false.
And:
func(5).equals(5)
would output: true
This is the code I have written:
const func = (obj) => {
const obj2 = {
not: () => {
return !obj
},
equals: (num) => {
return obj === num
}
}
return obj2
}
It works when I call func(3).not() or func(5).equals(5), but doesn't allow me to chain the functions so calling func(5).not().equals(5) returns an error saying that this is not a function.
What am I not seeing here?
That's a very weird way to compose functions. Let's think about what's actually happening.
func(3).not().not().equals(4)
// is equivalent to
not(not(equals(4)(3)))
// where
const not = x => !x;
const equals = x => y => x === y;
The simplest way to implement this chain would be as follows.
const equals = x => toBool(y => x === y);
const toBool = func => ({
not: () => toBool(x => !func(x)),
func
});
const example1 = equals(4).not().not().func(3);
const example2 = equals(5).func(5);
console.log(example1); // false
console.log(example2); // true
However, this is a forward chain. You want a backward chain. Unfortunately, there's a problem.
In a forward chain .func(x) marks the end of the chain.
In a backward chain .equals(x) marks the end of the chain.
This means that in a backward chain, you wouldn't be able to write the following expression.
func(3).not().not().equals(4).add(1)
// expected to be equivalent to
not(not(equals(4)(add(1)(3))))
// but actually equivalent to
not(not(equals(4)(3))).add(1)
// which evaluates to
false.add(1)
On the other hand, you would be able to do this quite easily using a forward chain.
const id = x => x;
const toNum = func => ({
add: x => toNum(y => x + func(y)),
equals: x => toBool(y => x === func(y)),
func
});
const toBool = func => ({
not: () => toBool(x => !func(x)),
func
});
const { add, equals } = toNum(id);
const example1 = equals(4).not().not().func(3);
const example2 = add(1).equals(4).not().not().func(3);
console.log(example1); // false
console.log(example2); // true
By the way, this is an object-oriented design pattern even though it doesn't make use of classes.
My advice would be to write plain old functions.
const add = (x, y) => x + y;
const equals = (x, y) => x === y;
const not = x => !x;
const example1 = not(not(equals(4, 3)));
const example2 = not(not(equals(4, add(1, 3))));
console.log(example1); // false
console.log(example2); // true
The simplest solutions are usually the best. Source: Occam's razor.
To return another object with the same methods that wraps the new value, simply call func again:
const func = (obj) => {
const obj2 = {
not: () => {
return func(!obj)
// ^^^^^^^^^^^^^^^^^
},
equals: (num) => {
return obj === num
}
}
return obj2
}
console.log(func(3).not().not().equals(4))
console.log(func(5).equals(5))
console.log(func(3).not())
You can use a closure to store both the initial input and the state of the operation:
const func = (input) => {
let not = false
const obj = {
not: () => {
not = !not
return obj
},
equals: (num) => {
return not ? input !== num : input === num
}
}
return obj;
}
console.log(func(5).not().equals(5))
console.log(func(5).not().not().equals(5))
console.log(func(5).not().equals(4))
console.log(func(5).not().not().equals(4))
You could take an object for return as interface and store value and negation.
var object = {
func: function (value) {
object.left = value;
return object;
},
not: function() {
object.negation = !object.negation;
return object;
},
equals: function (value) {
var result = value === object.value;
return object.negation ? !result : result;
}
},
func = object.func;
console.log(func(3).not().not().equals(4));

How to dynamically select an object to a add property - js

I have two objects. I want to add new properties to them, but I want to select first which object to use. Here's the desired logic :
let myFn = (function() {
// The Two Objects
let university = {}
let Person = {}
let AddNewPropTo = ( objName , new_objProp ) => {
return objName[new_objProp] = null; // logic fail 1
}
let getProps = (objName) => {
return Object.getOwnPropertyNames(objName)
}
return {
AddNewPropTo,
getProps
}
})();
myFn.AddNewPropTo('Person','Age'); // logic fail 2
myFn.getProps(Person) // Desired Output : Person = { Age : null }
The error message at logic fail 1 tells you that you're trying to add a property to the string objName rather than the object.
So you need to select one of your objects,
let AddNewPropTo = ( objName , new_objProp ) => {
if (objName === 'Person') {
Person[new_objProp] = null
return Person
}
if (objName === 'university') {
university[new_objProp] = null
return university
}
}
The second logic fail looks like a really basic mistake, the return value isn't captured
const Person = myFn.AddNewPropTo('Person','Age'); // logic fail 2
const props = myFn.getProps(Person) // Desired Output : Person = { Age : null }
console.log(props)
If you want to get more dynamic, try this
let university = {}
let Person = {}
const map = {
'Person': Person,
'university': university
}
let AddNewPropTo = ( objName , new_objProp ) => {
let obj = map[objName]
if (obj) {
obj[new_objProp] = null
return obj
}
return {}
}
I am not familiar with typescript but i got the result as like you want
Check this fiddle
Code:
let Person = {}
function AddNewPropTo( objName , new_objProp ) {
return objName[new_objProp] = 1; // logic fail 1
}
function getProps (objName) {
return Object.getOwnPropertyNames(objName);
}
AddNewPropTo(Person,'Age');
console.log(getProps(Person)) ; //returns ['Age']

JS getting value of object with key starting with a string

Is there a quick way to get the value of a key starting with a certain string?
Example :
var obj = {
"key123" : 1,
"anotherkey" : 2
}
obj['key1'] // would return 1
obj['ano'] // would return 2
Thanks
You can create a helper function
function findValueByPrefix(object, prefix) {
for (var property in object) {
if (object.hasOwnProperty(property) &&
property.toString().startsWith(prefix)) {
return object[property];
}
}
}
findValueByPrefix(obj, "key1");
As Kenney commented, the above function will return first match.
You could use find on the entries of the object. IF there's a key which starts with, access the index at 1 to get the value.
Object.entries(o).find(([k,v]) => k.startsWith(part))?.[1]
Here's a snippet:
const getValue = (part, o) => Object.entries(o).find(([k, v]) => k.startsWith(part))?.[1]
const obj = {
"key123": 1,
"anotherkey": 2
}
console.log(
getValue('key', obj),
getValue('ano', obj),
getValue('something', obj),
)
Search for the first match of the property name which begins with specified string:
var obj = {
"key123": 1,
"anotherkey": 2,
"somekey" : 3
};
function getObjectValueByPartialName(obj, name){
if (typeof obj !== "object") {
throw new Error("object is required!");
}
for (var prop in obj) {
if (prop.indexOf(name) === 0){
return obj[prop];
}
}
return "no such property!";
}
console.log(getObjectValueByPartialName(obj, 'some')); // output: 3
console.log(getObjectValueByPartialName(obj, 'key1')); // output: 1
We could use the following one-line generic custom method
var obj = {
"key123" : 1,
"anotherkey" : 2
};
const getObjPropForMatchPrefixKey = (object,prefix) => Object.keys(object).filter(item => item.toString().startsWith(prefix))[0];
console.log(obj[getObjPropForMatchPrefixKey(obj,'an')]);
console.log(obj[getObjPropForMatchPrefixKey(obj,'key')]);

Categories