Return value from lambda expression - ecmascript-6

I have a trouble to understand the return value from line #14. My question is about: fn(acc), x in one line. How is it possible to write it in a block instead? Like: {return fn(acc), x} ?? Of course it wouldn't work. Does any one can write it down as a block code instead of a one-line code?
1 const scream = str => str.toUpperCase()
2 const exclaim = str => `${str}!`
3 const repeat = str => `${str} ${str}`
4
5 const string = 'Hello world'
6
7 // Nested
8 // const result1= repeat(exclaim(scream(string)))
9 // console.log(result1)
10 // HELLO WORLD! HELLO WORLD!
11
12 // Instead of nesting, compose your functions into a new function
13 const compose = (...fns) => x =>
14 fns.reduceRight((acc, fn) => fn(acc), x)
15
16 const enhance = compose(repeat, exclaim, scream)
17 const result2 = enhance(string)
18 console.log(result2)
19 // HELLO WORLD! HELLO WORLD!
Thanks!

The thing to note is mostly that reduceRight is a reverse for loop. Here is the same function with loops and regular function expressions:
const scream = str => str.toUpperCase()
const exclaim = str => `${str}!`
const repeat = str => `${str} ${str}`
const string = 'Hello world'
// (...fns) =>
function compose() {
var fns = arguments;
// x =>
return function(x) {
var acc = x;
// fns.reduceRight((acc, fn) =>
for(var i = fns.length-1; i>=0; i--) {
var fn = fns[i];
// fn(acc)
acc = fn(acc);
}
return acc;
}
}
const enhance = compose(repeat, exclaim, scream)
const result2 = enhance(string)
console.log(result2)

Related

loop always returns 0 when comparing 2 arrays

My code should return a percentage of similaritys between two arrays but it just returns 0 all the time. what am i missing here? I want to compare the original array with the mutated array
const returnRandBase = () => {
const dnaBases = ['A', 'T', 'C', 'G']
return dnaBases[Math.floor(Math.random() * 4)]
}
// Returns a random single strand of DNA containing 15 bases
const mockUpStrand = () => {
const newStrand = []
for (let i = 0; i < 15; i++) {
newStrand.push(returnRandBase())
}
return newStrand
}
const pAequorFactory = (num, arr) => {
return {
specimen: num,
dna: arr,
mutate() {
let ranNum = Math.floor(Math.random() * this.dna.length)
let newBase = returnRandBase()
while (this.dna[ranNum] === newBase) {
newBase = returnRandBase();
}
this.dna[ranNum] = newBase
return this.dna
},
compareDNA(pAequor) {
let matches = 0;
let length = this.dna.length
for (let i = 0; i< pAequor.length; i++) {
if (length[i] === pAequor[i]){
matches++
}
} const simliaritys = matches / length * 100
console.log(simliaritys.toFixed(2) + "%")
}
}
}
const test1 = pAequorFactory(1, mockUpStrand())
console.log(test1)
const test2 = test1.mutate()
console.log(test1.dna);
(test1.compareDNA(test2))
You change the value of test1's DNA within mutate() with this.dna[randNum] = newBase, so mutate() returns test1.dna which is equal to test2.

Why do Google Apps Script and Crypto.subtle generate different RSA signatures?

I can't figure out why these two pieces of code generate different signatures when using the same message and key as inputs. Does anyone know what I'm doing wrong?
I've tried putting \0 or \n at the end of either message.
I've tried using ASCII encoding instead of UTF-8 for Apps Script.
I've tried different salt lengths for crypto.subtle (but I think Apps Script must use salt length of 0, because it generates the same signature every time.)
Google AppsScript
function testSign() {
const message = "test";
const key = `-----BEGIN PRIVATE KEY-----
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
-----END PRIVATE KEY-----
`;
const sig = Utilities.computeRsaSha256Signature(message, key, Utilities.Charset.UTF_8);
console.log(sig.slice(0,4)); // [ 45, -76, -60, -59 ]
}
Crypto.subtle:
const message = "test";
const pemContents = `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`;
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function importRsaKey(pemContents) {
const binaryDerString = window.atob(pemContents);
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-PSS",
hash: "SHA-256"
},
true,
["sign"]
);
}
const key = await importRsaKey(pemContents);
const sig = await window.crypto.subtle.sign(
{
name: "RSA-PSS",
saltLength: 0,
},
key,
(new TextEncoder()).encode(message)
);
const sigArray = new Int8Array(sig);
console.log(sigArray[0], sigArray[1], sigArray[2], sigArray[3]); // -97 -106 92 29
Looks like the problem was the algorithm for crypto.subtle. Following code for crypto.subtle matches appsscript:
const message = "test";
const pemContents = `MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDfIp9fsgiWRBWfieygtQaC0glxm7B5TbJAIDRMe6V9AJnMfSQ9nYpej+P5xhRIlXGT/WCtI3o9l/mIkh4Q8wmJg/HOZEoOyqPq2xFHLc9lksp2GWaQjFed0AUG+6KOJEAk6GHa6iyd0QwhN6bynzlS6Qd8i3fo3OrB9z+4YUrp4WWprswi5ogc91ckopTZvOJR1R+skm7dJ8amVkD7+OMufOGQNMYumgzsUJAQolCzxzlvz7D1L80gQRHF6IAMy4VaQezi/gB/xOVrMZFPD1Rk/lGDV1UlQVn0oxDluuGM1XCdKEcecQfMP77r0RZ5Tu6AtI4MuMSYTzliyrpe8n+4uYfBxhpi2aMLIusiUGTfYlCMKpWVi05wq6t2IgKXAQQEEWWYVWYH5CNwocHBPFn1wKJxt+qkKJsz5K4DtvAGg09f9xnRPZyMK3ZJK0W4Shuhj+MtujAh3/g2VTfOkiy8PUlcJD/4kdv/jKudDhlJ866whBVK2zgufWR4n6xfuq0RRbYagAQCAZwMNkkYi9g0yWkkw7U3sfSYiNuIZPTCtTan37gqnCfDYPVD7XjNKaZ+1UOggFwkun0+qcezfl9+k8wSEAkSbjEPfUeTwuIvYYPYLNvMbsd1E2fpTO5pGknDmE5P2JJqcufkz3iXCunOw/BScE6UxuDMFMX05OL0XQIDAQABAoICAA8B1V26i9zNYfXrsRRC8JfqV/PB4J3b9aXd9J6DP2dXm8B21zHkr1p6S38wTQtvr6agJzkl1nIfj0sZ5rdFnUnYK7JxqNBzXRBt4OzcXiNK+t50CWOl6LumsrvcPzvXoM/KqFAwqUUI+wud4lbVkiWrIhOEjtFE0G4wuqKkOoVd4ThHFxgu0I9ALGZ8n83AKCmQT7PL2nR52SC1UuQPgnoNMJ+CCSU3u7BGH9ZakFpzBwAn5BMtfpqRfchFgZ06r/KY1f2TT0XIoJRzzj3WvlqXhzRx8npRuaLcN8X3qnVOIqeTPqtDt15LmEPkeWI5xo0194rP/3rt8yJbl6rMnP3XolkdKtxogVpjrr+ywbvacSHyFKj0hiCrPjoXQXJR7l1hEsvtA0D74quFUnuS6dyZkk6AsWxXE0Yk2SelnYUtvdiGUtuv72vqepFilCMRcVuh0HuBAhtJ9VsSSnDm009aREvRs4svwbYuP/ryKkDCIkMbnzQII1H42JJGiWd7IvgZFzqv3wM1OCTisaMzHsC/bcr4Kb12i+NQLtp23C0V4GTRM++5CGLuDiQRSF9Q8OGeccIGdbM/kjIND6G1R3wQUoXZ61Mw7rOOctYsOdZGnf9Ghl4kxaS/eg9yjkzmm5dzK0Vw6o6ipFX7dhEqoZj92f6kgwuLX8HOW9AnJQGxAoIBAQD4UV5dba9FXGR7wfdx37tIg/HxCDRxcT5wZC7h7TcoFJsCJF5gnSMdGWsUNwFerZ74YnS9TRV3VJjn94KEgT+NGpAVhKnNqHPjWbxuOXjl7zMV3HaUJDrvzs+2e6s/uMXIEx4adf6YUowIkCGHjwYEM3hF48vg88unqD8lUSBPsEKxj6CJGq+NKi/eh2bRp2DFb2gBoqSHEywU11CZ35PMUipOWm419dv7qQn62+6UOiv0DjxWJRNRxt3R/meBnv/SGsKSIfl13H1QQg+aSW9xaDwwet/neDWYl20M2pBUPWYBIbpaz5ni/0WZOceaSbxh2pXZJi/L/B4tY33aVl7xAoIBAQDmCc/5Hq2a0uPd4NJifsZhF7hmNZNUQhkiWpW7NbwdI4w69sS714Pvs0zF+wXx43wWcec6a4yLK0Qqay0CcwdqMI6IKVfjPCpohVVHTgsOmQHBnfrEq3VUsGA8wQjWPNW9d/u9mLtGZYSm2BWaIWDSdHQzx28SsrsTu4yvYxoZdhenXsD9a1QYvu0X+C3UCfOdYC5VzNfUc86ELpQRunOhF8rhbbZNectCcijgc69yz5BLXtyfqvfWmiUBkFz+yb0o5RfEk0SSo60LH7P7wjuirAfASXqkGa7XGXAIRxq+JmHB0hqr1SoyQdMpSm7t5ug8IXgV1TTwP0mUS4ImwIQtAoIBABR7scsHJzQTgP5sa5rrF6nNqIF3acwJyVrACNX+GVSnDnpIwbg6fhECbcDHIMfMjpZymKqc1y52vf40foGrn7BmBoif3tnmEVkpp8930i81YgNloipqKqppZtzoqqGg/j+YxBzuqsep139FVF64P4jNLhilx8WQlrYHvN25KW8pXPcEn/tvRhfg6P30MVkN83+VxwCLiALUZAh8Elv/A1QRWwgHkZvF4hWKRhZ5Wd7ERafmHLgGaueN/fI6iBM7KGMObgpb3xYH0BZ0vJC/if/S11QwbpPLaLBjnU04IjuwrN9fBt5CzbDZ2cXf7EUf2/g+banx6nNrIpof4NvH0CECggEAHgdL4b4ydVJwMmeFrxvTc5swFA+MUuRp+YUPpKeIDdm1FYFe/xJMA79JF1MEXKYQbbGiuIqPhx83v73L21T+s8rw4C9dbKlO8+Pr1OoIIXixtP+VW5TyNQLtHSEpsSWx1RDTiNVmJPNdlJYCg+M1i2NuQ9AV3L/+Eb5ayA5MuuQihFOnJ62aBbzuoEFiYhqGdZW3lrWtuur/G1wlMgc/ztiXQEQdFxH+CYdzdJFFZtxXfq88Z49e2OG4UPLyYMQe8DavmpaKzgWVsi0KRqP9Oufv/xbYbpF3tFZ6vGnjwMyr2CxAFQw3fOYA1ZQE1QNeb3MDBP6W8YGhbj1JGRvqZQKCAQA2pJwXlgGc2TmBkqQiu2udzO9kueNqmH47V9tccoS7pfAI7NUH/6MH5hYYc45P63A7LlPyBYcHyIbJBHCQ7s1x8bGy4+d3BnmmQ2bRGWPC5ONo2mwZL4b3hZwhNqT68P3WgwBHOuuTdJ+pMotGrqyKkkEOYQ61/0x8M1xVcoVPUMutIKXqoJ7GjcD8WYddralNr735hOT1oF/3bGGRcKL2WNZRuZpiZXeJj1pGYRHmW7Rdof4lcFwz0zhAVVNiF4/CaaQhgZNGWW6MyfTllqBQoX5IOXvH49jaqEq6QA+crbbd634az9g7C5qIwcbVSDrtgb28AM5IARjOvDbqcJn9`;
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function importRsaKey(pemContents) {
const binaryDerString = window.atob(pemContents);
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256"
},
true,
["sign"]
);
}
const key = await importRsaKey(pemContents);
const sig = await window.crypto.subtle.sign(
{
name: "RSASSA-PKCS1-v1_5"
},
key,
(new TextEncoder()).encode(message)
);
const sigArray = new Int8Array(sig);
console.log(sigArray[0], sigArray[1], sigArray[2], sigArray[3]); // 45 -76 -60 -59

Scraping <meta> content with Cheerio

Tanaike helped me with this amazing script using Cheerio. The original was for Letterboxd, but this one is to pull in a watchlist from Trakt.tv (sample watchlist).
As it is right now it pulls in the watched date and the title, but I'd also like to pull in the content from the meta tag for each item.
<meta content="8 Million Ways to Die (1986)" itemprop="name">
I tried using $('[itemprop="name"]').attr('content'); but it doesn't accept the .attr piece.
Here is the full script as it is now, returning the watched date in the Col1 and the title in Col2.
/**
* Returns Trakt watchlist by username
* #param pages enter the number of pages in the list. Default is 10
* #customfunction
*/
function TRAKT(pages=10) {
const username = `jerrylaslow`
const maxPage = pages;
const reqs = [...Array(maxPage)].map((_, i) => ({ url: `https://trakt.tv/users/`+ username +`/history/all/added?page=${i + 1}`, muteHttpExceptions: true }));
return UrlFetchApp.fetchAll(reqs).flatMap((r, i) => {
if (r.getResponseCode() != 200) {
return [["Values couldn't be retrieved.", reqs[i].url]];
}
const $ = Cheerio.load(r.getContentText());
const ar = $(`a.titles-link > h3.ellipsify, h4 > span.format-date`).toArray();
return [...Array(Math.ceil(ar.length / 2))].map((_) => {
const temp = ar.splice(0, 2);
const watchDate = Utilities.formatDate(new Date($(temp[1]).text().trim().replace(/T|Z/g, " ")),"GMT","yyyy-MM-dd");
const title = $(temp[0]).text().trim();
return [watchDate,title];
});
});
}
The values can be pulled with this, so I know there isn't any sort of blocking in play.
=IMPORTXML(
"https://trakt.tv/users/jerrylaslow/history",
"//meta[#itemprop='name']/#content")
Any help is appreciated.
In order to achieve your goal, in your script, how about the following modification?
Modified script:
function TRAKT(pages = 10) {
const username = `jerrylaslow`
const maxPage = pages;
const reqs = [...Array(maxPage)].map((_, i) => ({ url: `https://trakt.tv/users/` + username + `/history/all/added?page=${i + 1}`, muteHttpExceptions: true }));
return UrlFetchApp.fetchAll(reqs).flatMap((r, i) => {
if (r.getResponseCode() != 200) {
return [["Values couldn't be retrieved.", reqs[i].url]];
}
const $ = Cheerio.load(r.getContentText());
const ar = $(`a.titles-link > h3.ellipsify, h4 > span.format-date`).toArray();
return [...Array(Math.ceil(ar.length / 2))].map((_) => {
const temp = ar.splice(0, 2);
const c = $(temp[0]).parent('a').parent('div').parent('div').find('meta').toArray().find(ff => $(ff).attr("itemprop") == "name"); // Added
const watchDate = Utilities.formatDate(new Date($(temp[1]).text().trim().replace(/T|Z/g, " ")), "GMT", "yyyy-MM-dd");
const title = $(temp[0]).text().trim();
return [watchDate, title, c ? $(c).attr("content") : ""]; // Modified
});
});
}
When this modified script is run, the value of content is put to 3rd column. If you want to put it to other column, please modify return [watchDate, title, c ? $(c).attr("content") : ""];.

How can i fragment a long boolean query string into multiple small boolean queries

I have a long boolean expression that needs to be fragmented into multiple small boolean expressions. For example:
Original expression: 1 or (2 and 3) and (not 4 or 5) or (6 and (7 or 8 or (9 and 10)))
expression1: 1
expression2: 2 and 3 and not 4
expression3: 2 and 3 and 5
expression4: 6 and 7
expression5: 6 and 8
expression6: 6 and 9 and 10
if I am evaluating (expression1 or expression2 or expression3 or expression4 or expression5 or expression6), then I am getting my original expression.
Practically I have the expression up to 10000 characters with very complex and nested boolean expressions. Any suggestions or ideas?
It looks like what you are trying to do is convert the query into Disjunctive Normal Form. Not hard to do in principle (parse as AST, then apply equalities as per link) but the problem is that the size of your DNF representation can grow exponentially. So for arbitrary expressions on 10k characters you simply won't be able to do so (you'd run out of time & memory).
If you can use variables then you could refactor parts of the expression into variables and then use those variables in your expression. Here's an example
// initial complex boolean expression
const x = "blah";
if (x === "asdfasd" || (x !== "asdfasd" && x.length < 4)) {
// match
}
// save parts of the complex boolean expression into variables
const isCondition1 = x === "asdfasd";
const isCondition2 = x !== "asdfasd";
const isCondition3 = x.length < 4;
// use those variable
if (isCondition1 || (isCondition2 && isCondition3)) {
// match
}
// use the variables to continue simplifying the expression
const isCondition4 = isCondition2 && isCondition3;
if (isCondition1 || isCondition4) {
// match
}
// until you are left with one expression
const isCondition5 = isCondition1 || isCondition4;
if (isCondition5) {
// match
}
So
if (x === "asdfasd" || (x !== "asdfasd" && x.length < 4)) {
// match
}
Can be rewritten as
const isCondition1 = x === "asdfasd";
const isCondition2 = x !== "asdfasd";
const isCondition3 = x.length < 4;
const isCondition4 = isCondition2 && isCondition3;
const isCondition5 = isCondition1 || isCondition4;
if (isCondition5) {
// match
}
I'd also move those expressions into functions and organize those functions in separate folders/files.
isCondition1.js
module.exports = x => x === "asdfasd";
isCondition2.js
module.exports = x => x !== "asdfasd";
isCondition3.js
module.exports = x => x.length < 4;
isCondition4.js
const isCondition2 = require("isCondition2");
const isCondition3 = require("isCondition3");
module.exports = x => isCondition2(x) && isCondition3(x);
isCondition5.js
const isCondition1 = require("isCondition1");
const isCondition4 = require("isCondition4");
module.exports = x => isCondition1(x) || isCondition4(x);
index.js
const isCondition5 = require("isCondition5");
if (isCondition5(x)) {
// match
}

How to determine if variable is a Cheerio object?

How do you tell if something is a loaded cheerio object? For instance:
const cheerio = require('cheerio');
const html = '<div>';
const $ = cheerio.load(html);
const $div = $('div');
// if isCheerio func existed..
if (cheerio.isCheerio(html)) {} // return false
if (cheerio.isCheerio($)) {} // return true
if (cheerio.isCheerio($div)) {} // return true
I could just check the typeof for a string vs func, but that is just a hack.
You do it like this:
const cheerio = require('cheerio');
const html = '<div>';
const $ = cheerio.load(html);
const $div = $('div');
console.log(html.cheerio) // undefined
console.log($.cheerio) // undefined
console.log($().cheerio) // [cheerio object]
console.log($div.cheerio) // [cheerio object]
So you can then use these in conditionals, like:
if ($div.cheerio) {
console.log($div.get())
}