How output is different for similar code-block in javascript - javascript

Why output is different in following:
1st scenario: prints
https://appmagic.io/modern/1
https://appmagic.io/modern/1
let urlHash = {};
const rootURL = 'http://tinyurl.com/';
var encode = function(longUrl) {
let hash = Date.now().toString(36);
urlHash[hash] = longUrl;
return `${rootURL}${hash}`
};
var decode = function(shortUrl) {
return urlHash[shortUrl.substring(rootURL.length)]
};
let url1 = encode("https://appmagic.io/classic/1");
let url2 = encode("https://appmagic.io/modern/1");
console.log(decode(url1));
console.log(decode(url2));
2nd scenario: prints
https://appmagic.io/classic/1
https://appmagic.io/modern/1
let urlHash = {};
const rootURL = 'http://tinyurl.com/';
var encode = function(longUrl) {
let hash = Date.now().toString(36);
console.log({hash}); // difference in code
console.log({hash}); // difference in code
urlHash[hash] = longUrl;
return `${rootURL}${hash}`
};
var decode = function(shortUrl) {
return urlHash[shortUrl.substring(rootURL.length)]
};
let url1 = encode("https://appmagic.io/classic/1");
let url2 = encode("https://appmagic.io/modern/1");
console.log(decode(url1));
console.log(decode(url2));
My guess is:
Since Date.now() gives values in milisecond, without console (IO operation i.e. time consuming sync operations) they get evaluate in nano-second and hash remains same, So the similar output in 1st scenario
But if we are adding console (IO operation i.e. time consuming sync operations) it delay operation for more than millisecond and different output comes in 2nd scenario.
I'm not sure if my perception is correct. Can any-one provide better/correct explanation.
If my guess is right, how can I create collision free fast hash,
thinking to use window.performance.now() but it is also not available in all browsers

While the comments address your main concern that it's possible to generate the same hash due to the encode function running more than once in the same millisecond, I'd like to leave this here as an example of addressing that by deferring the hashing until a unique key is generated:
function Coder(rootUrl) {
const urlHash = {}
return {
encode(longUrl) {
let hash
do {
hash = Date.now().toString(36)
} while (urlHash[hash])
urlHash[hash] = longUrl
return `${rootUrl}${hash}`
},
decode(shortUrl) {
return urlHash[shortUrl.substring(rootUrl.length)]
}
}
}
// usage example
const { decode, encode } = Coder('http://tinyurl.com/')
const url1 = encode('https://appmagic.io/classic/1')
const url2 = encode('https://appmagic.io/modern/1')
console.log('url1 encoded', url1)
console.log('url2 encoded', url2)
console.log('url1 decoded', decode(url1))
console.log('url2 decoded', decode(url2))
With this you'd only be able to generate one hash per millisecond, but I suppose that isn't a bad trade-off.

Related

Duplicates from websocket

I am receiving a push from a websocket (Java server/servlet) every 5 seconds. This works fine, the json are transmitted and parsed and delivered as planned. However, there are two problems.
I need to clear the between every push or else the result just grows with the same (json) dataset over and over again.
I need to stop the handleMessage()-function duplication data.
This is the javascript code:
function handleMessage(message) {
document.getElementById('delegate_number').empty();
document.getElementById('delegate_name').empty();
document.getElementById('reservation_type').empty();
var delegates = JSON.parse(message);
for (var i=0; i<delegates.length; i++) {
const nodeDelegateNumber = document.getElementById("delegate_number");
const nodeDelegateName = document.getElementById("delegate_name");
const nodeReservationType = document.getElementById("reservation_type");
const cloneDelegateNumber = nodeDelegateNumber.cloneNode(true);
const cloneDelegateName = nodeDelegateName.cloneNode(true);
const cloneReservationType = nodeReservationType.cloneNode(true);
document.body.appendChild(cloneDelegateNumber);
document.body.appendChild(cloneDelegateName);
document.body.appendChild(cloneReservationType);
document.getElementById("delegate_number").innerHTML = delegates[i].delegate_number;
document.getElementById("delegate_name").innerHTML = delegates[i].name_last + ", " + delegates[i].name_first;
document.getElementById("reservation_type").innerHTML = delegates[i].reservation_type;
}
}
If my english is bad I apologize. It is not my way of being lazy.
Best regards.

Get File Name from list of URL's - Google Drive

So I'm needing to get the list of file names from a range of Google Drive URLs in a spreadsheet. Browsing around the net, I came across the code below. It works but only for the old style urls, which I heard Google changed in September 2021.
Note that links are not fully functional, please replace with real links to check!
The old style is:
https://drive.google.com/file/d/1GMUwYxZxsNpLiaYOiVMBwl41LpreQ-fc/view?usp=sharing
This works correctly from the code below.
What I'd like though is two things.
It should handle a range of a couple of columns, currently reading AE2:AE, and printing out on AM2:AM. What I'd like is to go through the range: AE2:AL and print out: AM2:AT
Secondly it should also handle the newer form urls:
https://drive.google.com/file/d/0B9EZQqsLDEqDUGlsdy1oVEtETGs/view?usp=sharing&resourcekey=0-h7HOcxayPaHJ5r6dAAslVQ
Current Code:
function getNames() {
var activeRange = SpreadsheetApp.getActiveSheet().getDataRange();
var height = activeRange.getHeight();
var links = SpreadsheetApp.getActiveSheet()
.getRange("AE2:AE" + height)
.getValues();
var nameValues = [];
links.forEach((row) => {
try {
var link = row[0];
var fileID = getIdFromLink(link);
var name = DriveApp.getFileById(fileID).getName();
nameValues.push([name]);
} catch (e) {
nameValues.push(["NO NAME FOUND"]);
}
});
var nameRange = SpreadsheetApp.getActiveSheet().getRange("AM2:AM" + height);
nameRange.setValues(nameValues);
}
function getIdFromLink(link) {
var regex = new RegExp(
/(?<=https:\/\/drive\.google\.com\/file\/d\/)(.+)(?=\/)/
);
return regex.exec(link)[0];
}
How should the code above be modified to enable what I'm wanting. Sorry, I tried a couple of if/else statements, but my Javascript knowledge is severely limited.
Any help would be greatly appreciated.
Current "screenshot" showing:
(1) - Old style url - correctly picking up file name (2)
(3) - New style url - not picking up file name (4)
Your getIdFromLink() function should work just fine as long as the files have not been shared in such a way that they require a resource key as well.
To work with resource keys, use DriveApp.getFileByIdAndResourceKey(), like this:
function getFileNamesByLink() {
const sheet = SpreadsheetApp.getActiveSheet();
const sourceRange = sheet.getRange('AE2:AL');
const targetRange = sheet.getRange('AM2');
const fileNames = sourceRange.getValues()
.map(row => row.map(link => getFileNameFromLink_(link)));
targetRange
.offset(0, 0, fileNames.length, fileNames[0].length)
.setValues(fileNames);
}
function getFileNameFromLink_(link) {
if (!link) {
return null;
}
const fileId = getIdFromLink_(link);
if (!fileId) {
return NaN;
}
let file;
try {
file = DriveApp.getFileById(fileId);
} catch (error) {
try {
file = DriveApp.getFileByIdAndResourceKey(fileId, getResourceKeyFromLink_(link));
} catch (error) {
return NaN;
}
}
return file.getName();
}
function getIdFromLink_(link) {
const match = String(link).match(/file\/d\/([-\w]+)/i);
return match ? match[1] : null;
}
function getResourceKeyFromLink_(link) {
const match = String(link).match(/resourcekey=([-\w]+)/i);
return match ? match[1] : null;
}
Note that the script may time out if you have thousands of links. If that happens, process the links in a piecemeal fashion, or see if the Advanced Drive Service works for you.

Make multiple asynchronous fetch requests and find out which request includes a string match

Within a Chrome Extension i make multiple get requests and want to find out for which of the get requests the result has a match with a given string.
Because of the asynchronous call my "logging" which url i am analysing is not up to date, when i make the string match.
What i tried:
Going for r.url. But i am not sure this can be done to handover the value together with r.text().
Also i tried to make nested .then() calls, but that didnt work out.
Reproducible / example Code:
urls = ["https://stackoverflow.com/questions/tagged/javascript", "https://stackoverflow.com/questions/tagged/python"];
for (var reqNr = 0; reqNr < urls.length; reqNr++) {
reqUrl = urls[reqNr];
var AjaxPromise = fetch(reqUrl);
console.log("reqUrl");
console.log(reqUrl);
AjaxPromise.then(r => r.text()).then(result => {
if(result.includes("javascript")){
console.log(result);
// This is the wrong Url now, because of the asynchronity
getUrl = reqUrl;
console.log("reqUrl2");
console.log(reqUrl); // will only show the last url in loop because of the asynchronity.
// I could take r.url, but i am not sure i can also pass it together with result: https://stackoverflow.com/questions/28703625/how-do-you-properly-return-multiple-values-from-a-promise.
console.log("Found match for url: ", reqUrl); // not the "correct" reqUrl
// console.log("Found match for url: ", r.url); DOESNT WORK
}
});
}
It might be solved dispensing the asychronity, but I would prefer to stay with the asynchronity due to Performance issues..
The most reliable way to do this is to store a unique result for each url.
async function fetchMatchesRegex(url) {
let response = await fetch(url);
let text = await response.text();
let matches = text.includes("javascript");
return {
url:url,
matches:matches
}
}
function runSearch() {
return new Promise((resolve,reject)=>{
let urls = ["https://stackoverflow.com/questions/tagged/javascript", "https://stackoverflow.com/questions/tagged/python"];
let promises = urls.map(url=>fetchMatchesRegex(url));
promises.forEach(promise=>promise.then(result=>{
if (result.matches) resolve(result.url);
}))
})
}
runSearch().then(url=>console.log(url));
EDIT: If you want to store all the results matching the regex, here's how
async function runMultiSearch() {
let urls = ["https://stackoverflow.com/questions/tagged/javascript", "https://stackoverflow.com/questions/tagged/python"];
let promises = urls.map(url=>fetchMatchesRegex(url));
let results = await Promise.all(promises);
return results.filter(result=>result.matches).map(result=>result.url);
}
runMultiSearch().then(urls=>console.log(urls));

Nodejs synchronous loops block execution

when I try to run a function in background it blocks every other requests until it is done...
For example if I execute that function and then try to make a get request to a route that returns some information from the database then the response will come only after that function execution is done and I don't understand why.
This is the basic structure of my function that runs in background (it finds the 3rd party requests from a page and then look for the initiator request for each of them):
const thirdPartyReq = [];
let allRequests = [];
const findInitiatorReq = async () => {
allRequests = allRequests.reverse();
for(const [_, request] of thirdPartyReq.entries()) {
if(!request["Initiator Request"]) {
const fullRequest = request['Request URL'];
const parseUrl = new URL(fullRequest);
let hostname = parseUrl.hostname || null;
const domain = await extractDomain(hostname);
let pathname = parseUrl.pathname || null;
hostname = hostname.replace(/www./g, '')
let checkUrl;
const domainIndex = hostname.indexOf(domain) - 1;
const subdomain = (hostname.substr(0, domainIndex));
const queryString = parseUrl.search || '';
const noProtocol = hostname + pathname + queryString;
const noQueryString = hostname + pathname;
const requestProcessing = [fullRequest, noProtocol, noQueryString, hostname];
const requestIndex = allRequests.findIndex((el) => {
return (el.url == request['Request URL'] && el.thirdParty);
});
for(const [_, query] of requestProcessing.entries()) {
for(const [index, checkRequest] of allRequests.entries()) {
if(index > requestIndex) {
if(checkRequest.content && checkRequest.content.body) {
const contentBody = checkRequest.content.body;
if(contentBody.includes(query)) {
request['Initiator Request'] = checkRequest.url;
}
}
}
}
}
}
}
}
for(const [pageIndex, page] of results.entries()) {
const pageUrl = page.url;
const requests = page.requests;
const savedRequestUrls = [];
let parseUrl = new URL(pageUrl);
let hostname = parseUrl.hostname;
let requestsCounter = 0;
const pageDomain = await extractDomain(hostname);
if(!urlList.includes(pageUrl)) {
crawledUrls.push(pageUrl);
}
for(const [_, request] of Object.entries(requests)) {
if(request.url.indexOf('data:') == -1) {
parseUrl = new URL(request.url);
hostname = parseUrl.hostname;
let requestDomain = await extractDomain(hostname);
const reqObj = await findThirdPartyReq(pageUrl, request, requestDomain);
if(reqObj != null) {
request.thirdParty = true;
savedRequestUrls.push(reqObj);
}
// Store all requests that have a domain
if(requestDomain) {
request.page = pageUrl;
allRequests.push(request);
requestsCounter++;
}
}
}
findInitiatorReq();
}
I noticed that everything will work well if I remove this part of code:
for(const [_, query] of requestProcessing.entries()) {
for(const [index, checkRequest] of allRequests.entries()) {
if(index > requestIndex) {
if(checkRequest.content && checkRequest.content.body) {
const contentBody = checkRequest.content.body;
if(contentBody.includes(query)) {
request['Initiator Request'] = checkRequest.url;
}
}
}
}
}
This is the route that calls the function:
router.get('/cookies',async (req, res) => {
res.status(200).send(true);
const cookies = await myFunc();
}
Can anyone please tell me why that function is blocking everything until it returns a response and how can I fix this?
The obvious answer here is to convert your function into an asynchronous one. There are already multiple answers here on StackOverflow about that topic.
The gist: use asynchronous functions when you're elaborating some heavy task. Bear in mind that NodeJS is single threaded, thus the fact that sync functions block execution of other functions, is somewhat expected.
The tool you need to use to achieve asynchronous functions are: async/await (included without libraries/transpiling in the latest NodeJS LTS) and Promises. Forget about callbacks, since they are a really bad design.
How to use async / await in js:
https://medium.com/#Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
https://medium.freecodecamp.org/how-to-write-beautiful-node-js-apis-using-async-await-and-the-firebase-database-befdf3a5ffee
How to use Promises and what they are:
Replacing callbacks with promises in Node.js
understanding javascript promise object
Well, obviously you have a synchronous loop, which, of course, blocks execution. It will eventually block it anyway, as it has to perform several heavy operations. The response to the client is sent, but you still continue to work on some stuff, so other requests will have to wait.
A probable solution could be something like triggering another node process and handling stuff out there (something similar to a WebWorker in the browser)
You can try this library: async, there is a eachSeries method in it, meant specifically for handling big chunks of data/arrays. See the documentation for further information

Using an online tool to generate a random name

I found this website http://www.mess.be/inickgenwuname.php
It allows you to type in a name and it will generate a random rapper name. I wanted to have a button on my website that just generates the name for you so I decided to write some javascript that will send a request to this website and parse the response to get the random name.
Here is the node.js code I wrote.
function getRandomName() {
var http = require('http');
var data = {
realname:"something"
};
var querystring = require("querystring");
var qs = querystring.stringify(data);
var qslength = qs.length;
var options = {
hostname: "www.mess.be",
path: "/inickgenwuname.php",
method: 'POST',
headers:{
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': qslength
}
};
var str = "";
var req = http.request(options, function(res) {
res.on('data', function (chunk) {
str+=chunk;
});
res.on('end', function() {
var s = str.slice(str.indexOf("From this day forward, I will be known as... ") + "From this day forward, I will be known as... ".length,
str.indexOf("-And you"));
s = s.replace("\n", "").trim();
console.log(s);
});
});
req.write(qs);
req.end();
}
When I went to the website and pressed f12 on chrome and inspected the code, I found this little segment...
So this is what I used to formulate the request to the php. However, I only guessed through trial and error that the data that needed to be send was key-value pair object where the key is realname. My question is, how would I have known this otherwise? Is there no way to find out from the website, where the data being send with the POST is being received from?
Why by guessing? The form tells you everything that needs to be sent.
Also you could press F12 -> Network, and then send the request. After this you look at the sent requests and search for a POST request. When you click on the /inickgenwuname.php request you get more information about it. In there you can see Response Headers / Request Headers and as a last category "Form Data". There you can see all the data that is sent with this form.
I hope this is the answer you were looking for.
Stealing bandwidth without proper compensation (so called web-scraping) is quite commonly frowned upon. I couldn't find anything on that site that allows for it although I did not search thoroughly.
Why don't you roll your own? It's very simple, as can be seen in this Q&D hack:
function wu_names(input){
// some nice, fitting adjectives. Add more
var adjectives = ["annoying", "crazy", "expert", "insane", "lucky", "sardonic", "pestering"];
// some nice, fitting nouns. Add more
var nouns = ["assassin", "bastard", "conjurer", "destroyer", "ninja", "prophet", "wizard"];
var first = "";
var second = "";
var hash = 0;
var primitive_hash = function(s){
var h = 0;
for(var i = 0;i < s.length;i++){
var c = s.charCodeAt(i);
// standard hash = hash * 31 + c
h = ((h << 5) - h>>>0) + c;
}
return h;
};
first = input.split(" ")[0];
// no useful entry at all
if(first === undefined){
return null;
}
hash = primitive_hash(first);
first = adjectives[hash % adjectives.length];
second = input.split(" ")[1];
// no second entry
if(second === undefined){
return null;
}
hash = primitive_hash(second);
second = nouns[hash % nouns.length];
return first + " " + second;
}
The lists of adjectives and nouns is quite short, you might add to them, as the comments suggest.

Categories