I'm going to begin by saying that this is purely a matter of syntax candy-
I have a number of javascript functions with this general signature:
const someFn = async (context, args) => {}
Each implementation deconstructs those two objects. For example:
const myHttpFn = async({req, res}, {id, name, potato}) => { ... }
Within the implementation, I'd like to be able to deconstruct the object in the signature (this makes it easy for users to see what arguments are truly required) but still have a reference to the rest of the object (some properties are pass-through). The closest I've come has been by wrapping both arguments in an object like so:
const { isCold, isDry } = require('./some/utilities');
const lunch({context, context:{fridge, pantry, sandwitchMaker}, {ingredientList, shouldHeat}) => {
const coldList = ingredientList.filter(isCold);
const dryList = ingredientList.filter(isDry);
const [cold, dry] = await Promise.all(
fridge.fetch(context, { list: coldList }),
pantry.fetch(context, { list: dryList })
);
return sandwitchMaker.cook(context, { cold, dry, shouldHeat });
}
const sandwitch = await lunch({context, {
ingredientList: ["bread", "cheese", "tomato", "bacon", "bacon", "lettuce", "mayo"],
shouldHeat: true
}});
Adding the wrapper object gives me access to both context as a whole as well as the pieces that I've deconstructed. Is there a way to do this without a wrapper object?
You can't do it without your so-called wrapper object, but you can have just the wrapper object in the function definition - something like
const lunch({context: {fridge, pantry, sandwitchMaker, ...other}, {ingredientList, shouldHeat}) => {
const context = {fridge, pantry, sandwitchMaker, ...other};
//... rest of your code
}
Related
I have a react state that contains an array of objects that look like this {ID: 7, LicenseNumber: 'Testing123', LicenseCreator: 7, added: true}
To manipulate the individual values of this object, I have prepared a function that looks like this:
const updateLicenseInfo = (index, licenseInfoKey, licenseInfoVal) => {
const foundLicenseInfo = { ...licenseInfo[index] };
const licenseInfoItems = [...licenseInfo];
foundLicenseInfo[licenseInfoKey] = licenseInfoVal;
licenseInfoItems[index] = foundLicenseInfo;
setLicenseInfo([...licenseInfoItems]);
};
My problem is when I run this function twice in a row, I believe the asynchronous nature of hooks only causes the last function to run.
For example, when I make an API call and then try to update the ID, then the added field, ID will be null but added will have changed. Here is that example:
postData('/api/licenseInfo/createLicenseInfoAndCreateRelationship', {
LicenseNumber,
RequestLineItemID: procurementLine.ID
})
.then((res) => res.json())
.then((r) => {
updateLicenseInfo(licenseIndex, 'ID', r.ID);
updateLicenseInfo(licenseIndex, 'added', true);
})
How Can I ensure that this function runs and then the next function runs synchronously
How about refactoring your initial function to take an array of objects as parameters, something with the structure {key: "", val: ""} and then iterating over that array in your function, adding all the values to the paired keys and calling setState only once, with all the new changes
const updateLicenseInfo = (index, pairs) => {
const foundLicenseInfo = { ...licenseInfo[index] };
const licenseInfoItems = [...licenseInfo];
pairs.forEach((pair)=>{
foundLicenseInfo[pair.key] = pair.value;
});
licenseInfoItems[index] = foundLicenseInfo;
setLicenseInfo([...licenseInfoItems]);
};
and called like this
updateLicenseInfo(licenseIndex, [{key:'ID', value:r.ID},
{key:'added', value:true}]);
Could someone possibly help me understand this function and destructuring a little better?
export default ({ names }) => {
const {
people: {
children = []
} = {}
} = names;
children.find((child) => child.email);
};
I understand that names is being destructured to extract data stored in objects in order to find the first child in the children's array that has an email address. But what I don't understand is why is this function declared like this ({ names })? Is it because names is an object? Sorry if this is a silly question but I am struggling to understand this.
Lets break it down:
Your function takes in 1 parameter which is an object...
...that object must have a property names.
Then the function destructures the object as follows (const { people: { children = [] } = {} } = names;):
First, you destructurize a property called people from the names argument
If people doesn't exist, it takes the default value of {}.
And finally, it grabs the property children (which are an array of
objects) from its parent people.
Next, the function logic with .find()
All it does is searching for a child from children from people from names from the argument object that has a property email...
...and returns it. Unfortunately that part is missing in your function code.
So in your snippet, the function actually does absolutely nothing, except to be unnecessarily complicated :P
To wrap things up. This is what your argument to the function could look like:
const argument = {
names: {
people: {
children: [{ email: "myemail#mail.com", name: "thechildtobefound" }],
}
}
};
And here's a working sample to try:
const func = ({ names }) => {
const { people: { children = [] } = {} } = names;
return children.find((child) => child.email);
};
const argument = {
names: {
people: {
children: [{ email: "myemail#mail.com", name: "thechildtobefound" }],
},
},
};
console.log(func(argument));
I am new to destructuring and need help finding the best solution. I have a pretty complicated object coming back as a response and would like to clean it up. Without doing anything, it looks like this:
const homeTeam = {
totalPlaysFor: res.data.stats.home.teamStats[0].miscellaneous.offensePlays,
totalPlaysAgainst: res.data.stats.away.teamStats[0].miscellaneous.offensePlays
}
I know I can do something like:
const { offensePlays } = res.data.stats.home.teamStats[0].miscellaneous;
but that only solves my problem for one of them and would leave the other still long and tough to read.
You can destructure the stats property in advance, then make a helper function that takes the .home or .away object and navigates to the nested .offensePlays:
const { stats } = res.data;
const getOffPlays = obj => obj.teamStats[0].miscellaneous.offensePlays;
const homeTeam = {
totalPlaysFor: getOffPlays(stats.home),
totalPlaysAgainst: getOffPlays(stats.away)
};
Without having a standalone helper function, you could also create the object by .mapping an array of properties (eg [['totalPlaysFor', 'home'], ['totalPlaysAgainst', 'away']]) and pass it to Object.fromEntries, but that would be significantly less readable IMO.
While you can destructure this directly, via something like
const {
data: {
stats: {
home: {
teamStats: [{
miscellaneous: {
offensePlays: totalPlaysFor
}
}]
},
away: {
teamStats: [{
miscellaneous: {
offensePlays: totalPlaysAgainst
}
}]
}
}
}
} = res
const homeTeam = {totalPlaysFor, totalPlaysAgainst}
that feels pretty ugly.
Regardless whether you think your code is ugly, I can see a much more important problem with it: it doesn't work when any of those properties doesn't exist. To solve that, you might want a feature not yet ubiquitous in the language, one of Optional Chaining that would avoid throwing errors for a nested path such as 'data?.stats?.home?.teamStats?.0?.miscellaneous?.offensePlays', simply returning undefined if one of the nodes mentioned does not exist.
An equivalent feature is available as a function in many libraries. Underscore's property, lodash's property, and Ramda's path offer slightly different versions of this. (Disclaimer: I'm a Ramda author.) However it's easy enough to write our own:
const getPath = (pathStr) => (obj) =>
pathStr .split ('.')
.reduce ((o, p) => (o || {}) [p], obj)
const res = {data: {stats: {home: {teamStats: [{miscellaneous: {offensePlays: "foo"}}]}, away: {teamStats: [{miscellaneous: {offensePlays: "bar"}}]}}}}
const homeTeam = {
totalPlaysFor: getPath ('data.stats.home.teamStats.0.miscellaneous.offensePlays') (res),
totalPlaysAgainst: getPath ('data.stats.away.teamStats.0.miscellaneous.offensePlays') (res)
}
console .log (homeTeam)
Note that the array is not delineated in this simple version as [0] but just as .0. Obviously we could make this more sophisticated if necessary.
You can destructure as much as you want!
const homeTeam = {
totalPlaysFor: res.data.stats.home.teamStats[0].miscellaneous.offensePlays,
totalPlaysAgainst: res.data.stats.away.teamStats[0].miscellaneous.offensePlays
}
You can even use a combination of array destructuring / object destructuring here:
const [ home ] = res.data.stats.home.teamStats
const [ away ] = res.data.stats.away.teamStats
const { offensePlays: totalPlaysFor } = home.miscellaneous
const { offensePlays: totalPlaysAgainst } = away.miscellaneous
const hometeam = { totalPlaysFor, totalPlaysAgainst }
Or, if you want a more reusable solution, you can use parameter destructuring:
const getTeam = (
{ miscellaneous: { offensePlays: totalPlaysFor } },
{ miscellaneous: { offensePlays: totalPlaysAgainst } }
) => ({
totalPlaysFor,
totalPlaysAgainst
})
Then you can use it like:
const [ home ] = res.data.stats.home.teamStats
const [ away ] = res.data.stats.away.teamStats
const homeTeam = getTeam(home, away)
How to work with db models in functional programming (js for example).
If i have regular orm the code will be similar to:
let users = await usersRepo.where({active: 1})
.limit(10)
.orderBy('dateCreated, 'asc')
.get()
// activate the users
let mails = []
for(let u of users) {
mails.push(u.activate());
}
In functional programming:
let users = users.get(db, {where: {active: 1}, order: 'dateadded'})
for(u of users){
usersFn.activate(db, u)
}
I have to pass db and each instance it causes confusion because i have to pass many arguments(db for example) since no state are being pass to the constructors.
Is there more convenient way because the OOP variant is way more pretty.
I want to use functional programming but not to sacrifice code beauty.
This is OOP:
class User {
constructor(db) {
this.db = db
}
get(constraints) {
}
}
let users = new User(db)
let result = users.get({where: {active: 1}, order: 'date'})
This is fp with higher order function:
const bindUsersGet = (db) => {
return (constraints) => {
// db is available here
// constraints as well
}
}
let boundUserGet = bindUsersGet(db)
let result = boundUserGet({ where: { active: 1 }, order: 'dateadded' })
I dont see how fp with higher order function is better than OOP variant.
It is the same except the syntax? What's so special about it?
Thanks
You could use the concept of a higher order function to generate a "bound" users that already knows the DB instance. Some rough code:
const bindUsersGet = (db) => {
return (constraints) => {
// db is available here
// constraints as well
}
}
let boundUserGet = bindUsersGet(db)
let result = boundUserGet({ where: { active: 1 }, order: 'dateadded' })
This is an example for a single function, but you can obviously also return an object with multiple functions all having access to the db given to the "outer" function.
This works…
const { prop1:val1, prop2:val2 ) = req.query
val1 = val1.toLowerCase()
Though, I'm more inclined to do something like
const { prop1.toLowerCase():val1, prop2:val2 } = req.query
or
const { prop1:val1.toLowerCase(), prop2:val2 } = req.query
neither of which work. Is there a syntax similar to this or must manipulations be done outside of the destructing assignment?
No, this is not possible. A destructuring assignment does only assign, it does not do arbitrary transformations on the value. (Setters are an exception, but they would only complicate this).
I would recommend to write
const { prop1, prop2:val2 ) = req.query;
const val1 = prop1.toLowerCase();
or, in one statement:
const { prop1, prop2:val2 ) = req.query, val1 = prop1.toLowerCase();
The trouble with the temporary variable solutions is that they introduce different versions of the same data into the scope, which can lead to bugs.
This solution creates a utility function that receives the object to be destructured as well as a second object that is a mapping of property names to transformation functions. It's a little more verbose, but does the trick.
// Utility functions to perform specified transformations on an object
function transformProps(obj, trans) {
return Object.assign({}, obj, ...Object.entries(trans).map(([prop, fn]) =>
prop in obj ? {[prop]: fn(obj[prop])} : null
));
}
const { prop1:val1, prop2:val2 } = transformProps(
{prop1: "FOO", prop2: "BAR"},
{prop1: v => v.toLowerCase()} // Transformations to be made
);
console.log(val1, val2);