I have a Vue 2 serverless web application which uses OpenLayers. I ran into an interesting programming issue that applies to other applications as well where I need to execute 3 methods in sequence many times.
for(let i = 0 ; i < this.dateArraySurface10.length - 1 ; i++)
{
waterfall([
this.setTimeSurface10(),
this.map.renderSync(),
this.myCallback(),
],
function(err){
console.log("Waterfall done",err);
});
}
This is my attempt to call the three functions which do the following three things
this.setTiimeSurface10() : updates the TIME parameter of the ImageWMS source layer already added to the map.
this.map.renderSync() : is a OpenLayers method called on the global map object to ensure all layers are rendered.
this.myCallback() : is a function that extracts the map canvas and adds it to a GIF object as a frame.
My problem is I need those three methods to run in that sequence 72 times and although I can hardcode them with setTimeout I need a more abstract way of doing this so I can allow users to add many layers and export to GIF no matter what. I have tried to add an event listener on the this.map object but something is not working properly.
Programmatically how would one ensure all three methods are excuted in sequence inside the for loop in the most pure Javascript way?
In case it helps here are the two methods :
setTimeSurface10: function () {
if (this.currentTimeSurface10 === null) {
this.currentTimeSurface10 = this.startTimeSurface10;
} else if (this.currentTimeSurface10 >= this.endTimeSurface10) {
this.currentTimeSurface10 = this.startTimeSurface10;
} else {
this.currentTimeSurface10 = new Date(
this.currentTimeSurface10.setMinutes(this.currentTimeSurface10.getMinutes() + 60)
);
}
this.surface10.getSource().updateParams({ TIME: this.currentTimeSurface10.toISOString().split(".")[0] + "Z" });
},
myCallback: function () {
const mapCanvas = document.createElement('canvas');
const divElement = document.querySelector(".map");
mapCanvas.width = divElement.offsetWidth;//size[0];
mapCanvas.height = divElement.offsetHeight;//size[1];
const mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
const transform = canvas.style.transform;
const matrix = transform
.match(/^matrix\(([^\(]*)\)$/)[1] //eslint-disable-line
.split(',')
.map(Number);
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix);
mapContext.drawImage(canvas, 0, 0);
}
}
);
this.gif.addFrame(mapCanvas, {copy:true, delay: 200});
}
Answered at my other question but here is the answer again.
Thanks to some help from Mr. Hocevar at OpenLayers (whom I suggest you support if you can on Github Sponsor) I got an answer for anyone interested.
async mapToCanvasList() {
for(let i = 0 ; i < this.dateArraySurface10.length - 1 ; i++)
{
this.setTimeSurface10();
await new Promise(resolve => this.map.once('rendercomplete', resolve));
this.myCallback();
}
this.gif.on('finished', function(blob) {
window.open(URL.createObjectURL(blob));
});
this.gif.render();
},
myCallback: function () {
const mapCanvas = document.createElement('canvas');
const divElement = document.querySelector(".map");
mapCanvas.width = divElement.offsetWidth;//size[0];
mapCanvas.height = divElement.offsetHeight;//size[1];
const mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
const transform = canvas.style.transform;
const matrix = transform
.match(/^matrix\(([^\(]*)\)$/)[1] //eslint-disable-line
.split(',')
.map(Number);
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix);
mapContext.drawImage(canvas, 0, 0);
}
}
);
this.gif.addFrame(mapCanvas, {copy:true, delay: 200});
}
As you can see rendering the entire method asynchronous and adding an await promise for the rendercomplete event ensures that the loop waits and executes myCallback which adds the rendered context as a frame to the GIF object.
Related
Using Web MIDI andTeoria.JS I'm trying to build a web based a chords controller.
I found a way to generate chords for a scale thanks to teoria-chord-progression and then get the midi codes for it. Now I would like to get the midi notes for the inversions of the same chord.
What I have done so far is subtracting 12 from the original midi note for the fifth for the first inversion then for the third and the fifth for the second inversion but I'm sure there is a better way.
EDIT: here the code I have, it plays only the chord in its not inverted form:
'use strict';
const teoria = require('teoria');
const chordProgression = require('teoria-chord-progression');
const Combokeys = require("combokeys");
const document = global.document;
const cSharpHMinor = teoria.scale('c#', 'harmonicminor');
const chords = chordProgression(cSharpHMinor, [1, 2, 3, 4, 5, 6, 7], 3).chords;
const combokeys = new Combokeys(document.documentElement);
global.navigator.requestMIDIAccess()
.then((midiAccess) => {
return Array.from(midiAccess.outputs.values());
})
.then((midiOutputs)=> {
chords.forEach((chord, index) => {
buildPadForChord(index + 1, chord, midiOutputs);
});
});
function createPad(index, chordName, listener) {
let button = document.createElement('button');
button.setAttribute('type', 'button');
button.textContent = `${chordName} (${index})`;
button.addEventListener('click', listener);
let autorepeat = false;
combokeys.bind(index.toString(), () => {
if (!autorepeat) {
autorepeat = true;
listener();
}
}, 'keydown');
combokeys.bind(index.toString(), () => {
autorepeat = false;
}, 'keyup');
document.documentElement.appendChild(button);
}
function buildPadForChord(index, chord, midiOutputs) {
let listener = () => {
midiOutputs.forEach((midiOutput) => {
chord.notes().forEach((note)=> {
midiOutput.send([0x90, note.midi(), 127]);
midiOutput.send([0x80, note.midi(), 127], global.performance.now() + 1000.0);
});
});
};
createPad(index, chord.name, listener);
}
According to this issue on git, inversions currently are not implemented in Teoria.js. At one point the developer was considering adding them, but nothing so far.
My implementation in my own code was this:
// Adds an "invert" function to the Teoria Chord class
teoria.Chord.prototype.invert = function(n){
// Grabs the current chord voicing. Returns array of intervals.
var voicing = this.voicing()
// Reverse the array to work top-down when n is negative
if(n < 0){
voicing = voicing.reverse()
}
// Loop through each inversion
var j = Math.abs(n);
for(let i = 0; i < j; i++){
// Find which interval we're modifying this loop.
let index = i % voicing.length;
if(n > 0){
// Move the lowest note up a perfect 8th
voicing[index] = voicing[index].add(teoria.interval('P8'))
}else{
// Move the highest note down a perfect 8th
voicing[index] = voicing[index].add(teoria.interval('P-8'))
}
}
// Change the array into a usable format
var newVoicing = arrayToSimple(voicing)
this.voicing(newVoicing)
// Return the object for chaining
return this;
}
The arrayToSimple function:
function arrayToSimple(array){
var newArray = []
for(let item of array){
newArray.push(item.toString())
}
return newArray
}
Now, you can call Chord.invert(n) on any chord, where n is your inversion number. If n is negative, the chord will be inverted downwards, whereas if it is positive, it will be inverted upwards.
Note that the above invert() function needs a chord with a voicing of [lowest note, ..., highest note], in that order. Consequently, calling Chord.invert(1).invert(1) will not be the same as calling Chord.invert(2). If you need this functionality, you might detect which notes are lowest using Chord.bass() and/or Chord.notes(), and reorder the elements. That is probably overkill, though.
I am trying to animate through CSS properties by adding "start" and "end" classNames through javascript. I have defined a bunch of these classes in CSS (for instance starting with opacity: 0 and ending with opacity: 1
I am trying to loop through all of the elements, set a start className on an element, then an end className on the element to trigger the transition. The problem is if I do that how I normally would, by the time the function finishes there would be no actual className change.
Using setTimeout with even no delay is one way to get around this, as was mentioned here but that method does not work in this instance because of my looping. I am using a closure.
Here is my JS code:
var animation = (function () {
var a = {};
a.divIndex = -1;
a.imgIndex = startHolders.length;
a.animIndex = -1;
a.divs = startHolders;
a.run = function () {
if (++a.divIndex === a.divs.length) a.divIndex = 0;
if (++a.animIndex === animations.length) a.animIndex = 0;
if (++a.imgIndex === imageElements.length) a.imgIndex = 0;
imageElements[a.imgIndex].className = animations[a.animIndex]['start'];
setTimeout(function() {
imageElements[a.imgIndex].className = animations[a.animIndex]['end'];
}, 0);
startHolders[a.divIndex].appendChild(imageElements[a.imgIndex]);
};
setInterval(a.run, 1000);
return a;
})();
What I really wanted was to be able to set all those indexes to 0 and use this instead of a (just some placeholder object) but I couldn't do this with the setTimeout because of how it changes the value of this. Another problem here is that if I put the if(++a.index) at the bottom of the code, by the time setTimeout runs the value ofa.index` has changed (I believe) Does anybody know of a workaround here or just a better way?
If you would like access to this inside of the function passed to setTimeout you can use bind.
For example:
var obj = {}
obj.divIndex = "do you wanna build a snowman?";
obj.run = function () {
console.log(this.divIndex);
}
setTimeout(obj.run.bind(obj), 1000);
I'm thinking you might want to do something like this?:
var a = {};
a.divIndex = -1;
a.imgIndex = startHolders.length;
a.animIndex = -1;
a.divs = startHolders;
a.run = function () {
if (++this.divIndex === this.divs.length) this.divIndex = 0;
if (++this.animIndex === animations.length) this.animIndex = 0;
if (++this.imgIndex === imageElements.length) this.imgIndex = 0;
imageElements[this.imgIndex].className = animations[this.animIndex]['start'];
setTimeout(function() {
imageElements[this.imgIndex].className = animations[this.animIndex]['end'];
}.bind(this), 0);
startHolders[this.divIndex].appendChild(imageElements[this.imgIndex]);
};
setInterval(a.run.bind(a), 1000);
I'm wondering if anyone has an easy solution for this. I'm trying to detect if any part of a HTML element finds itself outside of the viewport. I've tried utilizing the following code:
$.fn.isOnScreen = function(){
var win = $(window);
var viewport = {
top : win.scrollTop(),
left : win.scrollLeft()
};
viewport.right = viewport.left + win.width();
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.right = bounds.left + this.outerWidth();
bounds.bottom = bounds.top + this.outerHeight();
Brought to you by Steven
I can only get this to work when the entire element is not viewable anymore, but I just need to know if part of the element is outside of the viewport.
When the element is outside of the viewport, then I'm putting a different class on it, so that it will shift to the left instead so that it is viewable again.
Something like:
if(elementIsPartiallyOutsideViewport) {
ele.addClass('move-left');
}
Any ideas?
Most of the browsers already support getBoundingClientRect() method. So you can try the following code.
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
You simply pass the element to the function and get false if element is not inside the viewport.
Usage.
if (!isElementInViewport(el)) {
el.addClass('move-left');
}
Edit
Just an addition. You can get more info about getBoundingClientRect() function and the browser support in here
There is a solution with an Intersection Observer.
An advantage of the solution is position-relative
calculations are not required.
jQuery plugin code snippet: (plugin.js)
(function ($) {
class IntersectionDetector {
async isElementPartiallyOutOfViewport(element) {
return new Promise((resolve) => {
const callback = this.handleOutOfViewportObservation.bind(this, resolve);
const options = {
root: null,
threshold: 0,
}
const observer = new IntersectionObserver(callback, options);
observer.observe(element);
})
}
handleOutOfViewportObservation(resolve, [entry], observer) {
const element = entry.target;
observer.unobserve(element);
observer.disconnect();
const ratios = new Map();
ratios.set('inViewportCompletelyRatio', 1);
ratios.set('outOfViewportCompletelyRatio', 0);
ratios.set('actualRatio', entry.intersectionRatio);
const precision = Math.pow(10, 2);
for (const [name, prevRatio] of ratios) {
const nextRatio = precision * prevRatio;
ratios.set(name, nextRatio);
}
const actualRatio = ratios.get('actualRatio');
const inViewportCompletelyRatio = ratios.get('inViewportCompletelyRatio');
const outOfViewportCompletelyRatio = ratios.get('outOfViewportCompletelyRatio');
const isOutOfViewportPartially =
(actualRatio > outOfViewportCompletelyRatio) &&
(actualRatio < inViewportCompletelyRatio);
resolve(isOutOfViewportPartially);
}
}
$.fn.isOnScreen = async function () {
const elements = this;
const promises = elements.map(async (index, element) => {
const detector = new IntersectionDetector();
return await detector.isElementPartiallyOutOfViewport(element);
});
const results = await Promise.all(promises);
return results.every(result => result);
};
}(jQuery));
Usage example: (main.js)
jQuery(async function ($) {
const isOnScreen = await $('#element-to-check').isOnScreen();
console.log(isOnScreen);
});
Explanation:
jQuery operates nodes collection, thus a better option is to
check every node in a collection. If every node in the collection
is partially visible the function returns true.
The observer has specified a root argument equal to null,
which means the element's intersection is being detected relative to a browser window.
The solution uses precision. Potentially, JavaScript can
cause a mistake while processing floating point numbers.
The idea is to compare integer parts of numbers
instead of floats to avoid the incorrect result.
For instance, there is a good answer to this issue:
https://stackoverflow.com/a/50780164/11173494
The code tested for:
jQuery 3.6.*
Chrome 103.0.* / Firefox 108.0
I have a piece of JavaScript code that I am executing using the node.js interpreter.
for(var i = 1; i < LIMIT; i++) {
var user = {
id: i,
name: "MongoUser [" + i + "]"
};
db.users.save(user, function(err, saved) {
if(err || !saved) {
console.log("Error");
} else {
console.log("Saved");
}
});
}
How can I measure the time taken by these database insert operations? I could compute the difference of date values after and before this piece of code but that would be incorrect because of the asynchronous nature of the code.
Use the Node.js console.time() and console.timeEnd():
var i;
console.time("dbsave");
for(i = 1; i < LIMIT; i++){
db.users.save({id : i, name : "MongoUser [" + i + "]"}, end);
}
end = function(err, saved) {
console.log(( err || !saved )?"Error":"Saved");
if(--i === 1){
console.timeEnd("dbsave");
}
};
There is a method that is designed for this. Check out process.hrtime(); .
So, I basically put this at the top of my app.
var start = process.hrtime();
var elapsed_time = function(note){
var precision = 3; // 3 decimal places
var elapsed = process.hrtime(start)[1] / 1000000; // divide by a million to get nano to milli
console.log(process.hrtime(start)[0] + " s, " + elapsed.toFixed(precision) + " ms - " + note); // print message + time
start = process.hrtime(); // reset the timer
}
Then I use it to see how long functions take. Here's a basic example that prints the contents of a text file called "output.txt":
var debug = true;
http.createServer(function(request, response) {
if(debug) console.log("----------------------------------");
if(debug) elapsed_time("recieved request");
var send_html = function(err, contents) {
if(debug) elapsed_time("start send_html()");
response.writeHead(200, {'Content-Type': 'text/html' } );
response.end(contents);
if(debug) elapsed_time("end send_html()");
}
if(debug) elapsed_time("start readFile()");
fs.readFile('output.txt', send_html);
if(debug) elapsed_time("end readFile()");
}).listen(8080);
Here's a quick test you can run in a terminal (BASH shell):
for i in {1..100}; do echo $i; curl http://localhost:8080/; done
Invoking console.time('label') will record the current time in milliseconds, then later calling console.timeEnd('label') will display the duration from that point.
The time in milliseconds will be automatically printed alongside the label, so you don't have to make a separate call to console.log to print a label:
console.time('test');
//some code
console.timeEnd('test'); //Prints something like that-> test: 11374.004ms
For more information, see Mozilla's developer docs on console.time.
Surprised no one had mentioned yet the new built in libraries:
Available in Node >= 8.5, and should be in Modern Browers
https://developer.mozilla.org/en-US/docs/Web/API/Performance
https://nodejs.org/docs/latest-v8.x/api/perf_hooks.html#
Node 8.5 ~ 9.x (Firefox, Chrome)
// const { performance } = require('perf_hooks'); // enable for node
const delay = time => new Promise(res=>setTimeout(res,time))
async function doSomeLongRunningProcess(){
await delay(1000);
}
performance.mark('A');
(async ()=>{
await doSomeLongRunningProcess();
performance.mark('B');
performance.measure('A to B', 'A', 'B');
const measure = performance.getEntriesByName('A to B')[0];
// firefox appears to only show second precision.
console.log(measure.duration);
// apparently you should clean up...
performance.clearMarks();
performance.clearMeasures();
// Prints the number of milliseconds between Mark 'A' and Mark 'B'
})();
https://repl.it/#CodyGeisler/NodeJsPerformanceHooks
Node 12.x
https://nodejs.org/docs/latest-v12.x/api/perf_hooks.html
const { PerformanceObserver, performance } = require('perf_hooks');
const delay = time => new Promise(res => setTimeout(res, time))
async function doSomeLongRunningProcess() {
await delay(1000);
}
const obs = new PerformanceObserver((items) => {
console.log('PerformanceObserver A to B',items.getEntries()[0].duration);
// apparently you should clean up...
performance.clearMarks();
// performance.clearMeasures(); // Not a function in Node.js 12
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
(async function main(){
try{
await performance.timerify(doSomeLongRunningProcess)();
performance.mark('B');
performance.measure('A to B', 'A', 'B');
}catch(e){
console.log('main() error',e);
}
})();
For anyone want to get time elapsed value instead of console output :
use process.hrtime() as #D.Deriso suggestion, below is my simpler approach :
function functionToBeMeasured() {
var startTime = process.hrtime();
// do some task...
// ......
var elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
console.log('It takes ' + elapsedSeconds + 'seconds');
}
function parseHrtimeToSeconds(hrtime) {
var seconds = (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
return seconds;
}
var start = +new Date();
var counter = 0;
for(var i = 1; i < LIMIT; i++){
++counter;
db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
if( err || !saved ) console.log("Error");
else console.log("Saved");
if (--counter === 0)
{
var end = +new Date();
console.log("all users saved in " + (end-start) + " milliseconds");
}
});
}
Old question but for a simple API and light-weight solution; you can use perfy which uses high-resolution real time (process.hrtime) internally.
var perfy = require('perfy');
function end(label) {
return function (err, saved) {
console.log(err ? 'Error' : 'Saved');
console.log( perfy.end(label).time ); // <——— result: seconds.milliseconds
};
}
for (var i = 1; i < LIMIT; i++) {
var label = 'db-save-' + i;
perfy.start(label); // <——— start and mark time
db.users.save({ id: i, name: 'MongoUser [' + i + ']' }, end(label));
}
Note that each time perfy.end(label) is called, that instance is auto-destroyed.
Disclosure: Wrote this module, inspired by D.Deriso's answer. Docs here.
You could also try exectimer. It gives you feedback like:
var t = require("exectimer");
var myFunction() {
var tick = new t.tick("myFunction");
tick.start();
// do some processing and end this tick
tick.stop();
}
// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.myFunction.min()); // minimal tick duration
console.log(t.timers.myFunction.max()); // maximal tick duration
console.log(t.timers.myFunction.mean()); // mean tick duration
console.log(t.timers.myFunction.median()); // median tick duration
[edit] There is an even simpler way now to use exectime. Your code could be wrapped like this:
var t = require('exectimer'),
Tick = t.Tick;
for(var i = 1; i < LIMIT; i++){
Tick.wrap(function saveUsers(done) {
db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
if( err || !saved ) console.log("Error");
else console.log("Saved");
done();
});
});
}
// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.saveUsers.min()); // minimal tick duration
console.log(t.timers.saveUsers.max()); // maximal tick duration
console.log(t.timers.saveUsers.mean()); // mean tick duration
console.log(t.timers.saveUsers.median()); // median tick duration
You can use a wrapper function to easily report the execution time of any existing function.
A wrapper is a used to extend an existing function to do something before and after the existing function's execution - and is a convenient way to compose logic.
Here is an example of using the withDurationReporting wrapper:
// without duration reporting
const doSomethingThatMayTakeAWhile = async (someArg: string, anotherArg: number) => {
/** your logic goes here */
}
// with duration reporting
const doSomethingThatMayTakeAWhileWithReporting = withDurationReporting(
'doSomethingThatMayTakeAWhile',
doSomethingThatMayTakeAWhile
);
// note: you can define the function with duration reporting directly, too
const doSomethingThatMayTakeAWhile = withDurationReporting(
'doSomethingThatMayTakeAWhile',
async (someArg: string, anotherArg: number) => {
/** your logic goes here */
}
)
And here is the wrapper itself:
import { hrtime } from 'process';
const roundToHundredths = (num: number) => Math.round(num * 100) / 100; // https://stackoverflow.com/a/14968691/3068233
/**
* a wrapper which reports how long it took to execute a function, after the function completes
*/
export const withDurationReporting = <R extends any, T extends (...args: any[]) => Promise<R>>(
title: string,
logic: T,
options: {
reportingThresholdSeconds: number;
logMethod: (message: string, metadata?: Record<string, any>) => void;
} = {
reportingThresholdSeconds: 1, // report on anything that takes more than 1 second, by default
logMethod: console.log, // log with `console.log` by default
},
) => {
return (async (...args: Parameters<T>): Promise<R> => {
const startTimeInNanoseconds = hrtime.bigint();
const result = await logic(...args);
const endTimeInNanoseconds = hrtime.bigint();
const durationInNanoseconds = endTimeInNanoseconds - startTimeInNanoseconds;
const durationInSeconds = roundToHundredths(Number(durationInNanoseconds) / 1e9); // https://stackoverflow.com/a/53970656/3068233
if (durationInSeconds >= options.reportingThresholdSeconds)
options.logMethod(`${title} took ${durationInSeconds} seconds to execute`, { title, durationInSeconds });
return result;
}) as T;
};
I designed a simple method for this, using console.time() & console.timeEnd():
measure function definition
function measureRunningTime(func,...args){
const varToString = varObj => Object.keys(varObj)[0]
const displayName = func.name || varToString({ func })
console.time(displayName)
func(...args)
console.timeEnd(displayName)
}
To use it, pass a function without arguments, with arguments binded, or with arguments as the following parameters.
Examples:
let's say I want to check the running time of the simplest searching algorithm - SimpleSearch:
measured function definition (your code here)
const simpleSearch = (array = [1,2,3] ,item = 3) => {
for(let i = 0; i< array.length; i++){
if (array[i] === item) return i;
}
return -1
}
implementation without arguments
measureRunningTime(simpleSearch)
//Prints something like that-> simpleSearch: 0.04ms
implementation with arguments using .bind()
const array = [1,2,3]
const item = 3
measureRunningTime(simpleSearch.bind(null, array, item))
//Prints something like that-> bound simpleSearch: 0.04ms
implementation with arguments without using .bind()
const array = [1,2,3]
const item = 3
measureRunningTime(simpleSearch, array, item)
//Prints something like that-> simpleSearch: 0.04ms
-> Take notice!! this implementation is far from perfect - for example there is no error handling - but it can be used to check the running times of simple algorithms,
Moreover , I'm not an experienced programmer so take everything with a grain of salt 🧂 👌
I had same issue while moving from AWS to Azure
For express & aws, you can already use, existing time() and timeEnd()
For Azure, use this:
https://github.com/manoharreddyporeddy/my-nodejs-notes/blob/master/performance_timers_helper_nodejs_azure_aws.js
These time() and timeEnd() use the existing hrtime() function, which give high-resolution real time.
Hope this helps.
I need this to be cumulative, and to measure different stuff.
Built these functions:
function startMeasuring(key) {
measureTimers[key] = process.hrtime();
}
function stopMeasuring(key) {
if (!measures[key]) {
measures[key] = 0;
}
let hrtime = process.hrtime(measureTimers[key]);
measures[key] += hrtime[0] + hrtime[1] / 1e9;
measureTimers[key] = null;
}
Usage:
startMeasuring("first Promise");
startMeasuring("first and second Promises");
await new Promise((resolve) => {
setTimeout(resolve, 1400);
});
stopMeasuring("first Promise");
stopMeasuring("first and second Promises");
startMeasuring("first and second Promises");
await new Promise((resolve) => {
setTimeout(resolve, 600);
});
stopMeasuring("first and second Promises");
console.log("Measure Results", measures);
/*
Measusre Results {
setting: 0.00002375,
'first Promise': 1.409392916,
'first and second Promise': 2.015160376
}
*/
I have a full path to an image, which I am using jQuery to read like this:
$('img.my_image').attr('src');
However I just want the filename portion (i.e. without full path).
Are there any built-in functions to do this, or would a regex be the only option?
var Filename= path.split('/').pop()
var fileNameIndex = yourstring.lastIndexOf("/") + 1;
var filename = yourstring.substr(fileNameIndex);
function getFileName(path) {
return path.match(/[-_\w]+[.][\w]+$/i)[0];
}
I found a better version handling unix and windows-like path-strings.
Number 1:
var unix_path = '/tmp/images/cat.jpg';
console.log(unix_path.replace(/^.*[\\\/]/, ''));
var win_path = 'c:\\temp\images\cat.jpg';
console.log(win_path.replace(/^.*[\\\/]/, ''));
Output will be cat.jpg
Number 2: (maybe faster)
var unix_path = '/tmp/images/cat.jpg';
console.log(unix_path.split(/[\\\/]/).pop());
var win_path = 'c:\\temp\images\cat.jpg';
console.log(win_path.split(/[\\\/]/).pop());
Output will be cat.jpg
In Javascript you could do
function getFileNameFromPath(path) {
var ary = path.split("/");
return ary[ary.length - 1];
}
Using this solution you can get both names i.e. with and without file extension.
//getting image source
var path=$('img.my_image').attr('src');
//splitting url and getting filename with file extension
var file=path.split('/').pop();
//removing extension and keeping just the filename
var filename=file.split('.').shift();
Addition to accepted answer. Looks like here is fastest and cross platform (Unix and Windows) solution:
function basePath(path) {
return path.substr(
Math.max(
path.lastIndexOf('\\'),
path.lastIndexOf('/'),
) + 1,
);
}
It's necessary in situation, when you have data from both Unix and Windows and we have to parse in in one place.
This function just takes latest of all possible separators and returns string after last separator. It is much faster with big strings and that's why:
no regexps, they are slower then simple equality
no normalization, have no unconditionally go threw whole the string and make something new from it
no creating any new instances, like new arrays and so on, it takes some time and memory
no extra cycles or even lookups because we don't use arrays here
Tested in Chromex87:
// Test subjects
//=====================
function basePathUnix(path) {
return path.split('/').pop();
}
function basePathUnix2(path) {
const arr = path.split('/');
return arr[ arr.length - 1 ];
}
function basePathUnix3(path) {
return path.substr(path.lastIndexOf('/') + 1);
}
function basePathCrossPlatform(path) {
return path.replace(/^.*[\\\/]/, '');
}
function basePathCrossPlatform2(path) {
return path.split(/[\\\/]/).pop();
}
function basePathCrossPlatform3(path) {
return path.substr(Math.max(path.lastIndexOf('\\'), path.lastIndexOf('/')) + 1);
}
function basePathCrossPlatform4(path, separators = ['/', '\\']) {
return path.substr(Math.max(...separators.map(s => path.lastIndexOf(s))) + 1);
}
// Tests
//=====================
function measureTime(name, fn) {
const start = window.performance.now();
for (let i = 0; i < 10000; i++) {
fn();
}
const time = window.performance.now() - start;
console.log(name, time);
}
function testResults(name, path) {
console.log('\n[CHECK RESULTS]', name);
console.log('basePathUnix:\t\t', basePathUnix(path));
console.log('basePathUnix2:\t\t', basePathUnix2(path));
console.log('basePathUnix3:\t\t', basePathUnix3(path));
console.log('basePathCrossPlatform:\t', basePathCrossPlatform(path));
console.log('basePathCrossPlatform2:\t', basePathCrossPlatform2(path));
console.log('basePathCrossPlatform3:\t', basePathCrossPlatform3(path));
console.log('basePathCrossPlatform4:\t', basePathCrossPlatform4(path));
}
function testPerformance(name, path) {
console.log('\n[MEASURE PERFORMANCE]', name);
measureTime('basePathUnix:\t\t', () => basePathUnix(path));
measureTime('basePathUnix2:\t\t', () => basePathUnix2(path));
measureTime('basePathUnix3:\t\t', () => basePathUnix3(path));
measureTime('basePathCrossPlatform:\t', () => basePathCrossPlatform(path));
measureTime('basePathCrossPlatform2:\t', () => basePathCrossPlatform2(path));
measureTime('basePathCrossPlatform3:\t', () => basePathCrossPlatform3(path));
measureTime('basePathCrossPlatform4:\t', () => basePathCrossPlatform4(path));
}
function runTest(name, path) {
setTimeout(() => {
testResults(name, path);
setTimeout(() => {
testPerformance(name, path);
}, 200);
}, 200);
}
// Run tests
//=====================
setTimeout(() => {
const pathUnix = '/some/path/string/some/path/string/some/path/string/some/path/string/some/path/string/some/path/file-name';
runTest('UNIX', pathUnix);
}, 1000);
setTimeout(() => {
const pathWind = '\\some\\path\\string\\some\\path\\string\\some\\path\\string\\some\\path\\string\\some\\path\\file-name';
runTest('WINDOWS', pathWind);
}, 2000);