How to use Ramda compose with more than two functions? - functional-programming

It's my first time trying out functional programming with Ramda. I am trying to build an api endpoint string by composing multiple functions.
This works:
const endpoint = str => str && str.toUpperCase() || 'default'
const protocol = str => `https://${str}`
let finalEndpoint = R.compose(protocol, endpoint)
var result = finalEndpoint('api.content.io')
result is now https://API.CONTENT.IO as expected
But now I want to add more functions to this pipeline:
const params = val => `?sort=desc&part=true`
const query = val => `query={ some: value, another: value}`
But when I try to compose everything together like this:
let finalEndpoint = R.compose(protocol, endpoint, params, query)
var result = finalEndpoint('api.content.io')
I just get https://?SORT=DESC&PART=TRUE whereas I wanted
https://API.CONTENT.IO??sort=desc&part=true&query={ some: value, another: value}
What combination of chaining and composition do I use to get the above result?

A variation of this that might seem familiar to Ramda users would be to write this in a pipeline of anonymous functions:
const finalEndpoint = pipe(
or(__, 'default'),
concat('https://'),
concat(__, '?sort=desc&part=true&'),
concat(__, 'query={ some: value, another: value}')
)
finalEndpoint('api.content.io');
//=> https://api.content.io?sort=desc&part=true&query={ some: value, another: value}
Such a points-free version might or might not be to your taste, but it's an interesting alternative.

var R = require('ramda');
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const params = str => `${str}?sort=desc&part=true&`
const query = str => `${str}query={ some: value, another: value}`
let finalEndpoint = R.compose(query, params, protocol, endpoint)
var result = finalEndpoint('api.content.io')
console.log(result)
Turns out I wasn't using the str parameter correctly

Related

Uploaded image URL not being stored in firestore the first time I setDoc

I am trying to store the downloadLink from firebase's storage into firestore. I am able to set all the data, and I am able to set the link, the second time I click the "post" button.
I know the issue has to do with asynchronous functions, but I'm not experienced enough to know how to solve the issue.
In the "createPost" function, I am console logging "i am the URL: {url}" and in the "uploadFile" function, I am console logging "look at me {url}" to debug.
I noticed the "I am the URL" outputs nothing and then shortly after, the "look at me" outputs the URL.
setDoc() of course stores the imageLink as an empty string.
What can I do to solve this? Any help would be greatly appreciated or any documentation to help with my understanding of async functions.
Here is my code:
const PostModal = (props) => {
const makeid = (length) => {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const [descriptionText, setDescriptionText] = useState("");
const [addressText, setAddressText] = useState("");
const [venueText, setVenueText] = useState("");
const [startTimeText, setStartTimeText] = useState("");
const [endTimeText, setEndTimeText] = useState("");
const [shareImage, setShareImage] = useState("");
const [videoLink, setVideoLink] = useState("");
const [assetArea, setAssetArea] = useState("");
const [url, setURL] = useState("");
const { data } = useSession();
const storage = getStorage();
const storageRef = ref(storage, `images/${makeid(5) + shareImage.name}`);
const uploadFile = () => {
if (shareImage == null) return;
uploadBytes(storageRef, shareImage).then( (snapshot) => {
//console.log("Image uploaded")
getDownloadURL(snapshot.ref).then( (URL) =>
{
setURL(URL);
console.log(`look at me: ${URL}`)});
});
}
const createPost = async () => {
var idLength = makeid(25);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadFile()
console.log(`I am the URL: ${url} `)
setDoc(doc(db, "posts", idLength), {
eventDescription: descriptionText,
eventAddress: addressText,
venueName: venueText,
startTime: startTimeText,
endTime: endTimeText,
imageLink: url,
videoLink: videoLink,
username: data.user.name,
companyName: !data.user.company ? "" : data.user.company,
timestamp: Timestamp.now(),
});
}
const handleChange = (e) => {
const image = e.target.files[0];
if(image === '' || image === undefined) {
alert('not an image, the file is a ${typeof image}');
return;
}
setShareImage(image);
};
const switchAssetArea = (area) => {
setShareImage("");
setVideoLink("");
setAssetArea(area);
};
const reset = (e) => {
setDescriptionText("");
setAddressText("");
setVenueText("");
setStartTimeText("");
setEndTimeText("");
setShareImage("");
setVideoLink("");
setURL("");
props.handleClick(e);
};
This was taken from a reddit user who solved my answer. Big thank you to him for taking the time to write out a thoughtful response.
So, you're kinda right that your issue has a bit to do with asynchronicity, but it's actually got nothing to do with your functions being async, and everything to do with how useState works.
Suffice it to say, when you call uploadFile in the middle of your createPost function, on the next line the value of url has not yet changed. This would still be true even if uploadFile were synchronous, because when you call a useState setter function, in this case setURL, the getter value url doesn't change until the next time the component renders.
This actually makes perfect sense if you stop thinking about it as a React component for a moment, and imagine that this was just vanilla JavaScript:
someFunction () {
const url = 'https://www.website.com';
console.log(url);
anotherFunction();
yetAnotherFunction();
evenMoreFunction();
console.log(url);
}
In this example, would you ever expect the value of url to change? Probably not, since url is declared as const, which means if the code runs literally at all, it's physically impossible for the value of url to change within a single invocation of someFunction.
Functional components and hooks are the same; in a single "invocation" (render) of a functional component, url will have the same value at every point in your code, and it's not until the entire functional component re-renders that any calls to setURL would take effect.
This is an extremely common misunderstanding; you're not the first and you won't be the last. Usually, it's indicative of a design flaw in your data flow - why are you storing url in a useState to begin with? If you don't need it to persist across distinct, uncoupled events, it's probably better to treat it like a regular JavaScript value.
Since uploadBytes returns a promise, you could make uploadFile asynchronous as well, and ultimately make uploadFile return the information you need back to createPost, like this:
const uploadFile = async () => {
if (shareImage == null) return;
const snapshot = await uploadBytes(storageRef, shareImage);
// console.log("Image uploaded")
const URL = await getDownloadURL(snapshot.ref);
return URL;
};
All I've done here us un-nest your .then calls, pulling the trapped values out into the usable scope of your uploadFile function. Now, you can change that one line of createPost to this:
const url = await uploadFile();
and eliminate your useState altogether.

I don't know how to wait for the execution of complex function using await Promise.all to complete

const fetchPrice = async (symbols) => {
const prices = {};
await Promise.all(
symbols.map(async (symbol) => {
const priceInformation =
(base.id, await base.fetch_ticker((Symbol = replacedSymbol)));
prices[symbol] = priceInformation;
})
);
return prices;
};
const PriceObj = await fetchPrice(SYMBOL_LIST);
console.log(PriceObj);
In the above code, I don't know how to wait for fetchPrice to be processed before executing console.log();.
The console.log(); only returns unexpected values, such as undefined or Promise.
(If I put console.log(prices); before return at the end of the fetchPrice function, then only this console.log(); returns the expected value, so the fetchPrice function itself is working).
I saw some information somewhere that return new Promise should be used, but it was too complicated for a beginner to understand.
How can I wait for fetchPrice to process and then display the last console.log(PriceObj); value correctly?
Here's a runnable version of your code where I've simulated fetch_ticker() as an asynchronous function that returns a promise that resolves in a random amount of time and modified some things in your code that seemed odd or incorrect.
function randInt(min, max) {
const r = Math.random();
return Math.floor((r * (max - min)) + min);
}
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
// simulate base.fetch_ticker
// resolves in a random amount of time with a random integer value
const base = {
fetch_ticker() {
let t = randInt(100, 1000);
return delay(t, t);
}
}
const fetchPrice = async (symbols) => {
const prices = {};
await Promise.all(symbols.map(async (symbol) => {
const priceInformation = await base.fetch_ticker(symbol);
prices[symbol] = priceInformation;
}));
return prices;
};
async function run() {
const SYMBOL_LIST = ["IBM", "GM", "TSLA", "GOOG"];
const PriceObj = await fetchPrice(SYMBOL_LIST);
console.log(PriceObj);
}
run().then(result => {
console.log("done");
}).catch(err => {
console.log(err);
});
As you can see in my comments to your question, there were parts of your code that didn't make sense to me. It is unclear what you're trying to do with the (Symbol = replacedSymbol) part of this since Symbol is a built-in global and replacedSymbol is not shown in your code at all and it's unclear why you would be doing this assignment in the middle of passing arguments:
base.fetch_ticker((Symbol = replacedSymbol))
And, it's unclear why the base.id is involved in this:
const priceInformation =
(base.id, await base.fetch_ticker((Symbol = replacedSymbol)));
This statement:
(base.id, await base.fetch_ticker((Symbol = replacedSymbol)))
will just evaluate to the the value of the second item in the comma list and the base.id will have no effect at all.
In my code example above, I've removed both of those elements since there's no explanation or reasoning provided for them.

How to use firebase query in actions-on-google

I want to use firebase query to search for a particular user by their name in my action-on-google app.I have used the following code but it dosen't prints anything.
const ref = firebase.database();
const nm = ref.child('Users');
const q =
nm.orderByChild('Name').equalTo('abcd');
q.on('value', snap => {
conv.ask(snap.val());
});
});
Can somebody help in rectifying my code.
Keep in mind that Javascript is pretty asynchronous, meaning that you need to make sure that your function understands the flow of your execution and more importantly know when it ends.
The standard way of doing this is through a Promise. Many async functions now return Promises.
So you can rewrite your code as:
app.intent('intent name', conv => {
const ref = firebase.database();
const nm = ref.child('Users');
const q = nm.orderByChild('Name').equalTo('abcd');
return q.once('value', snap => {
conv.ask(snap.val());
});
});

Reuse wildcard value in Firebase Functions

I'm checking the onUpdate of a {postId}, and I want to run a firebase database call with that same {postId}.. if that makes sense. Here is my code:
exports.handleVoteKarma = functions.database
.ref('upvotes/{postId}')
.onUpdate(async change => {
const scoreBefore = change.before.val() || 0;
const scoreAfter = change.after.val();
//This {postId} should be the same as the one above for the upvotes/{postId}
adb.ref('{item}/{loc}/{postId}/score').once('value').then((usr) => {
});
return null;
});
Essentially I want the {postId} in upvotes/ to have the same value as the {postId} when I check the score.. will it work like this?
Realtime Database triggers accept a second argument, which you're not using in your function:
exports.handleVoteKarma = functions.database
.ref('upvotes/{postId}')
.onUpdate(async (change, context) => {
// note the "context" parameter here
});
This is an EventContext object and it contains a params property with the values of the wildcards in the path. You'd use it simply like this:
const postId = context.params.postId
You can then use the postId string later to build other refs.
There is more discussion in the documentation.

what should each chained function return in redux-observable?

answering my own question:
it makes sense that the epic (at the end) should return a stream of its own
but what about the chained function calls in between? Can I return plan objects and then return a stream at the end?
do I need to return the observable itself or the subscription object?
for example:
is this idiomatic "rxjs" or redux-observable?
const epic = ($action, store) =>
action$
.filter(filterFunction)
.map(action => processAction(action, store))
.map(processResult)
.flatMap(apiCall)
.map(res => ({ type: 'DONE', payload: res }))
const processAction = (action, store) =>
Observable.create(
obs => {
const result = // do something with store and action
return obs.next(result)
})
const processResult = result =>
result.subscribe(res => {
const newRes = // do some other stuff
return Observable.from({ newRes })
})
epic: takes actionStream, filters for X type, maps each type to a diff operation, packages and sends request body to server, informs reducer that server call was successful
processAction: receives actions of X type, map each action to a process that compares snapshots (from the store) and outputs the cumulative DIFF between state trees.
processResult: receives DIFF and creates payload request body
That would not be idiomatic rxjs because map is supposed to be side effect free; in your example it's being abused quite heavily. I don't believe it entirely does what you intend.
I'm happy to suggest some patterns if you want to describe what you'd like to do :)

Resources