Javascript simplify array indexing as arguments - javascript

I have the following piece of code which works 100% but I can't help but feel their is a better way or more simple way of writing this or maybe not 😄all and any help is always appreciated 👍🏼
const entity: any = response.organisation;
if (Array.isArray(responseKey)) {
// responseKey e.g. ['site', 'accounts']
// I feel this two declarations can be refactored
const list = this.flattenGraphqlList<T>(entity[responseKey[0]][responseKey[1]]);
const {totalCount} = entity[responseKey[0]][responseKey[1]];
return {list, totalCount};
}
const list = this.flattenGraphqlList<T>(entity[responseKey]);
const {totalCount} = entity[responseKey];
return {list, totalCount};

Don't do everything twice:
const entity: any = response.organisation;
const object = Array.isArray(responseKey)
? entity[responseKey[0]][responseKey[1]] // responseKey e.g. ['site', 'accounts']
: entity[responseKey];
const list = this.flattenGraphqlList<T>(object);
const {totalCount} = object;
return {list, totalCount};
I guess you can find a more descriptive name than object :-)
For the last lines, I personally would prefer not to use destructuring, but that's more a stylistic choice:
return {
list: this.flattenGraphqlList<T>(object),
totalCount: object.totalCount
};

Related

Assign and Query Javascript Arrow Function for Metadata

The problem is rather simple. We need to imbue a function with a parameter, and then simply extract that parameter from the body of the function. I'll present the outline in typescript...
abstract class Puzzle {
abstract assign(param, fn): any;
abstract getAssignedValue(): any;
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
Let's set the scene:
Assume strict mode, no arguments or callee. Should work reasonably well on the recent-ish version of v8.
The function passed to assign() must be an anonymous arrow function that doesn't take any parameters.
... and it's alsoasync. The assigned value could just be stored somewhere for the duration of the invocation, but because the function is async and can have awaits, you can't rely on the value keeping through multiple interleaved invocations.
this.getAssignedValue() takes no parameters, returning whatever we assigned with the assign() method.
Would be great to find a more elegant solution that those I've presented below.
Edit
Okay, we seem to have found a good solid solution inspired by zone.js. The same type of problem is solved there, and the solution is to override the meaning of some system-level primitives, such as SetTimeout and Promise. The only headache above was the async statement, which meant that the body of the function could be effectively reordered. Asyncs are ultimately triggered by promises, so you'll have to override your Promise with something that is context aware. It's quite involved, and because my use case is outside of browser or even node, I won't bore you with details. For most people hitting this kind of problem - just use zone.js.
Hacky Solution 2
class HackySolution2 extends Puzzle {
assign(param: any, fn: AnyFunction): AnyFunction {
const sub = Object(this);
sub["getAssignedValue"] = () => param;
return function () { return eval(fn.toString()); }.call(sub);
}
getAssignedValue() {
return undefined;
}
}
In this solution, I'm making an object that overrides the getAssignedValue() method, and re-evaluates the source code of the passed function, effectively changing the meaning of this. Still not quite production grade...
Edit.
Oops, this breaks closures.
I don't know typescript so possibly this isn't useful, but what about something like:
const build_assign_hooks = () => {
let assignment;
const get_value = () => assignment;
const assign = (param, fn) => {
assignment = param;
return fn;
}
return [assign, get_value];
};
class Puzzle {
constructor() {
const [assign, getAssignedValue] = build_assign_hooks();
this.assign = assign;
this.getAssignedValue = getAssignedValue;
}
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
const puzzle = new Puzzle();
puzzle.test();
Hacky Solution 1
We actually have a working implementation. It's such a painful hack, but proves that this should be possible. Somehow. Maybe there's even a super simple solution that I'm missing just because I've been staring at this for too long.
class HackySolution extends Puzzle {
private readonly repo = {};
assign(param: any, fn) {
// code is a random field for repo. It must also be a valid JS fn name.
const code = 'd' + Math.floor(Math.random() * 1000001);
// Store the parameter with this code.
this.repo[code] = param;
// Create a function that has code as part of the name.
const name = `FN_TOKEN_${code}_END_TOKEN`;
const wrapper = new Function(`return function ${name}(){ return this(); }`)();
// Proceed with normal invocation, sending fn as the this argument.
return () => wrapper.call(fn);
}
getAssignedValue() {
// Comb through the stack trace for our FN_TOKEN / END_TOKEN pair, and extract the code.
const regex = /FN_TOKEN_(.*)_END_TOKEN/gm;
const code = regexGetFirstGroup(regex, new Error().stack);
return this.repo[code];
}
}
So the idea in our solution is to examine the stack trace of the new Error().stack, and wrap something we can extract as a token, which in turn we'll put into a repo. Hacky? Very hacky.
Notes
Testing shows that this is actually quite workable, but requires a more modern execution environment than we have - i.e. ES2017+.

Converting function with logic and ui into higher order functions

I'm trying to improve my JavaScript skills. I'm learning composability and functional patterns and I'm totally lost.
I have two functions: one mapping an array and the other called from within the previous function to generate the markup.
const names = ['peter', 'paul', 'patrice']
const namesMarkup = name => {
return `<p>${name}</p>`
}
const showNames = listOfNames => {
return listOfNames.map(el => {
return namesMarkup(el)
})
}
showNames(names)
I have been reading about HOF, which technically are functions that take a function as an argument and/or return a function.
How could I compose these functions to have a HOF?
I went through the basic examples like
const square = num => num * num
const plus10 = (num, callback) => {
return callback(num) + 10
}
console.log(addTwo(7, square))
but I cannot make my mind around the previous example and working with lists.
I will appreciate help since the more I research the more confused I get.
Your mistake is to assume an array for showNames. Never do this. Always implement the simplest version of a function. In FP array is a computational effect. Don't implement such an effectful function as default:
const nameMarkup = name => {
return `<p>${name}</p>`;
}
const nameMarkup2 = name => {
return `<p>${name.toUpperCase()}!</p>`;
}
const showName = f => name => {
const r = f(name);
/* do something useful with r */
return r;
}
const names = ['peter', 'paul', 'patrice']
console.log(
showName(nameMarkup) ("peter"));
// lift the HOF if you want to process a non-deterministic number of names:
console.log(
names.map(showName(nameMarkup2)));
Now swapping the markup just means to pass another function argument. Your showName is more general, because a HOF lets you pass part of the functionality.
If we drop the array requirement, your showNames doesn't do anything useful anymore. It still illustrates the underlying idea, though.

Destructuring assignment vs array.map

I am new at ReactJs and have a question about this two method:
1:
handleLike = movie => {
const movies = this.state.movies.map(m => {
if (m._id === movie._id) m.liked = !m.liked;
return m;
});
this.setState({ movies });
};
2:
handleLike = movie => {
const movies = [...this.state.movies];
const index = movies.indexOf(movie);
movies[index] = { ...movies[index] };
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
Q1: This two methods just toggle liked and work properly but i want to know there is any advantages or not?
Q2: What is purpose of this line in second method:
movies[index] = { ...movies[index] };
Don't use #1, at least not the way it is written. You are mutating the old state, which can easily cause bugs in react which assumes that state is immutable. You do create a new array, which is good, but you're not creating new elements inside the array. If you're changing one of the objects in the array, you need to copy that object before modifying it.
The better way to do #1 would be:
handleLike = movie => {
const movies = this.state.movies.map(m => {
if (m._id === movie._id) {
const copy = { ...m };
copy.liked = !m.liked;
return copy;
}
return m;
});
this.setState({ movies });
};
And that kind of gets to your question about #2 as well:
Q2: What is purpose of this line in second method:
movies[index] = { ...movies[index] };
The purpose of that is to make a copy of the movie. This lets you make a change to the copy, without modifying the old state.
Q1: This two methods just toggle liked and work properly but i want to know there is any advantages or not?
If you fix the mutation issue in #1, then it's pretty much a matter of preference.

Is there anyway to set a variable equal to function that is called everytime the variable is accessed

I am trying to re-create the 'useState' hook from React as a silly fun personal exercise, but am encountering trouble when accessing variables from my 'global state'. I know that this has no practical use outside of react, but I just thought it would be something to attempt regardless.
Currently I have the following implementation below, but because the destructured variable is set only that first time and is not updated when using the corresponding setter, it always will return the same variable. I fully understand why this is happening but I am unsure if there is a way to get this working at all or if this is a lost cause. The destructured setter does update the global state, but the variable is of course as previously mentioned not accessing the global state again since it is only set that initial time.
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = prop => {
const id = PROP_ID++;
GLOBAL_STATE[id] = prop;
return [
(() => {
return GLOBAL_STATE[id];
})(),
function(nv) {
GLOBAL_STATE[id] = nv;
}
];
};
const [userName, setUserName] = useState("Chris");
const [favCol, setFavCol] = useState("red");
console.log(GLOBAL_STATE);
console.log(userName);
setUserName("Bob");
console.dir(GLOBAL_STATE);
console.log(userName);
All I want to know if there is a way to set the destructured reference variable equal to some sort of function that will always be called to get the new variable from the global state when that variable is referenced.
I think you're missing a piece of the puzzle here.
React hooks depend on the position of their call within a given functional component. Without the encapsulating function, you remove the usefulness of the state being provided by the hook, because they're only being called once in your example, thus the reference in the destructuring syntax never gets updated as you observed.
Let's get them working in the context of functions.
const { component, useState } = (function () {
const functions = new WeakMap()
const stack = []
let hooks
let index
function component (fn) {
return function (...args) {
try {
stack.push({ hooks, index })
hooks = functions.get(fn)
index = 0
if (!hooks) {
functions.set(fn, hooks = [])
}
return fn.apply(this, args)
} finally {
({ hooks, index } = stack.pop())
}
}
}
function useState (initialValue) {
const hook = index++
if (hook === hooks.length) {
hooks.push(initialValue)
}
return [
hooks[hook],
function setState (action) {
if (typeof action === 'function') {
hooks[hook] = action(hooks[hook])
} else {
hooks[hook] = action
}
}
]
}
return { component, useState }
})()
const fibonacci = component(function () {
const [a, setA] = useState(1)
const [b, setB] = useState(1)
setA(b)
setB(a + b)
return a
})
const sequence = component(function () {
const [text, setText] = useState('')
setText(
text.length === 0
? fibonacci().toString()
: [text, fibonacci()].join()
)
return text
})
for (let i = 0; i < 20; i++) {
console.log(sequence())
}
The stack variable here allows us to nest our stateful function calls, and the hooks variable keeps track of the existing hook states by position within the currently executing component of the stack.
This implementation might seem overly-complicated, but the point of component() and stack is to partially mimic how the React framework treats functional components. This is still much simpler than how React works, because we're treating all calls of the same function as if it's the same instance of a functional component.
On the other hand, in React, a particular function could be used for several different instances, distinguishable from each other based on a number of factors such as the position in the hierarchy of the virtual DOM, the key and ref props, etc., so it's much more complicated than this.
It occurs to me you just want to get your example working. For that, all you need to do is change your variable to a getter function:
const useState = state => [
() => state,
value => { state = value }
];
const [getUserName, setUserName] = useState('Chris');
const [getFavCol, setFavCol] = useState('red');
console.log(getUserName());
setUserName('Bob');
console.log(getUserName());
Much simpler than what you had and doesn't require any globals to work.
If the manual getter seems too inconvenient, then you can't destructure, but you can implement an approach that's almost as easy to use:
const useState = state => ({
get state () { return state },
set (value) { state = value }
});
const userName = useState('Chris');
const favCol = useState('red');
console.log(userName.state);
userName.set('Bob');
console.log(userName.state);
This is a very interesting question.
The short answer is: no, that’s not possible*.
The long answer
The long answer is how JavaScript handles primitives and objects. Primitives values are copied during assignment (userName here: const [userName, setUserName] = useState("Chris"); which is a string), while in case of object a reference would be copied.
In order to play with it, I came with something like that (mind you, that is not solution to your challenge, rather explanation to my answer):
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = prop => {
const id = PROP_ID++;
GLOBAL_STATE[id] = {
_value: prop,
[Symbol.toPrimitive]() { return this._value },
}
const tuple = [
GLOBAL_STATE[id],
(nv) => GLOBAL_STATE[id]._value = nv,
];
return tuple;
};
const [userName, setUserName] = useState("Chris");
const [favCol, setFavCol] = useState("red");
console.log(GLOBAL_STATE);
console.log(userName);
console.log('set user name to:', setUserName("Bob"));
console.dir(GLOBAL_STATE);
console.log('' + userName);
GLOBAL_STATE entry is now object, so when you destructure it after calling useState only a reference is changed. Then update changes data inside this object but what we assigned in the first place is still there.
I added Symbo.toPrimitive property which coerses object to a primitive value but sadly, this will not work on it’s own. Only when run as '' + userName. Which means it behaves differently than you expected. At this point I stopped experimenting.
React
I went to Facebook’s Github and tried to trace what they are doing but gave up due to imports of imports of imports. Hence, I will take an educated guess here, based on Hooks API behaviour. I think that your implementation is rather faithful to the original. We use useState in a function and the value doesn’t change there. Only when state is changed and then the component is re-rendered with a new value, which again is assigned and won’t change.
*I will gladly welcome anyone who proves this notion wrong.
How about something along the following lines...
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = (varName, prop) => {
const id = PROP_ID++;
GLOBAL_STATE[id] = prop;
Object.defineProperty(window, varName, {
get: function(){
return GLOBAL_STATE[id];
}
});
return ((nv) => {
GLOBAL_STATE[id] = nv;
});
};
const setUserName = useState("userName", "Chris");
const setFavCol = useState("favCol", "red");
console.log(GLOBAL_STATE);
console.log(userName);
setUserName("Bob");
console.dir(GLOBAL_STATE);
console.log(userName);
Note that I've changed your interface a bit such that you have to pass the name of the variable to the useState function. Seems a bit kludgey, but allows a getter to be configured, in this case, following your example, on the global scope (ie, "window"), which might not be the best practice.
There is a solution, but only if you're okay using a dirty, dirty hack.
The following approach uses a with statement and a Proxy containing a custom get handler, and requires object destructuring syntax in order to determine the variable name from the property key of the setter function:
// initialize useState() hook with independent scope
function createHook (scope = Object.create(null)) {
const setter = /^set([A-Z][^\W_]*)$/;
function useState (initialValue) {
// return proxy from useState() so that object destructuring syntax
// can be used to get variable name and initialize setter function
return new Proxy(scope, {
get (target, propertyKey) {
if (!setter.test(propertyKey)) {
throw new TypeError(`Invalid setter name '${propertyKey}'`);
}
// get variable name from property key of setter function
const [, name] = propertyKey.match(setter);
const key = name[0].toLowerCase() + name.slice(1);
// support updater callback
const setState = value => {
target[key] = (
typeof value === 'function'
? value(target[key])
: value
);
};
// initialize state
setState(initialValue);
// return setter as property value
return setState;
}
});
}
return { scope, useState };
}
// example usage with a little magic
{
const { scope, useState } = createHook();
const { setFoo } = useState('bar');
console.log(scope.foo);
setFoo(42);
console.log(scope.foo);
}
// example use with more magic
const { scope, useState } = createHook();
with (scope) {
const { setUserName } = useState('Chris');
const { setFavCol } = useState('red');
console.log(userName, favCol);
setUserName('Bob');
setFavCol(color => `dark${color}`);
console.log(userName, favCol);
}
The following usage ends up being very similar to Jon Trent's answer by abusing implicit globals:
function createHook(e=Object.create(null)){var t=/^set([A-Z][^\W_]*)$/;return{scope:e,useState:n=>new Proxy(e,{get(e,r){if(!t.test(r))throw new TypeError(`Invalid setter name '${r}'`);var[,o]=r.match(t),c=o[0].toLowerCase()+o.slice(1),s=t=>{e[c]="function"==typeof t?t(e[c]):t};return s(n),s}})}}
const { useState } = createHook(window);
const { setUserName } = useState('Chris');
const { setFavCol } = useState('red');
console.log(userName, favCol);
setUserName('Bob');
setFavCol(color => `dark${color}`);
console.log(userName, favCol);

Trying to refactor this code to use only immutable structures

I'm a student of functional programming. I am still weaning myself off the old variable-mutation habits. But sometimes I get stuck. Ok, so here is the question--suppose we have the following closure
const bookShelf = () => {
let books = []
const listBooks = () => books
const addBook = (book) => {
books = books.concat(book)
return function removeBook() { books = books.filter( b => b !== book ) }
}
return {addBook,listBooks}
}
const { addBook, listBooks } = bookShelf()
const removeMobyDick = addBook('Moby Dick')
const removeWalden = addBook('Walden')
removeWalden()
console.log(listBooks()) // ["Moby Dick"]
Note that I have one object which is mutated: books.
My question is, how can I refactor this code so that books is immutable yet I achieve the same end-result. Feel free to use a functional library like Ramda if need be. My naive thought here is somehow use recursion to pass in a new value of books and then pass that version back. Seems a bit overreach so I thought to seek out help from someone more knowledgable in this arena.
Thanks for your insight!
Just leave book constant in your bookshelf. That will require creating a new bookshelf every time of course, so the easiest approach is to make books be a parameter of the function:
function bookShelf(books) {
return {
listBooks() { return books },
addBook(book) { return bookShelf(books.concat([book])); }
}
}
const empty = bookShelf([]);
const shelfWithMobyDick = empty.addBook('Moby Dick');
const shelfWithMobyDickAndWalden = shelfWithMobyDick.addBook('Walden');
console.log(shelfWithMobyDick.listBooks());
As you can see, there's no need for a removeBook function - you just use the old value that had not yet included the book.
If you want to be able to remove the book you just added from an arbitrary bookshelf, you can also return both the new bookshelf and a remover function:
…,
addBook(book) {
return {
bookShelf: bookShelf(books.concat([book]));
removeThis(shelf) { return bookShelf(shelf.listBooks().filter(b => b !== book)); }
};
}
to be used as
const empty = bookShelf([]);
const {bookShelf: shelfWithMobyDick, removeThis: removeMobyDick} = empty.addBook('Moby Dick');
const {bookShelf: shelfWithMobyDickAndWalden, removeThis: removeWalden} = shelfWithMobyDick.addBook('Walden');
const shelfWithWalden = removeMobyDick(shelfWithMobyDickAndWalden);
console.log(shelfWithWalden.listBooks());
The bookshelf type doesn’t really seem to be accomplishing anything here, so let’s just make it a list (array).
let bookshelf = [];
Now it looks like you want a way to produce a list with a new item and a way of removing that item from the list. A little weird, but you can do that by returning both in a tuple (array):
const remove = (list, value) =>
list.filter(x => x !== value);
const addRemovable = (list, value) =>
[[...list, value], list => remove(list, value)];
let bookshelf = [];
let removeMobyDick;
let removeWalden;
[bookshelf, removeMobyDick] = addRemovable(bookshelf, 'Moby Dick');
[bookshelf, removeWalden] = addRemovable(bookshelf, 'Walden');
bookshelf = removeWalden(bookshelf);
console.log(bookshelf);
This doesn’t look nice and you probably wouldn’t want to write something like it, but it does achieve the same thing as your original.

Categories

Resources