Turn a for loop on an object into a .map - javascript

I have been told my function:
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
Must now use a .map instead,
I have tried this below:
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But 1 I don't know if it's actually accurate, and also, it cant access the data outside of the scope of the function it is in, here is the whole function below:
public sessionData;
sessionDates(sessionsData) {
const temp = {};
this.sessionData = [];
sessionsData.forEach(session => {
const date = moment(session.startDatetime).format('DDMMYYYY');
if (temp[date]) {
temp[date].push(session);
} else {
temp[date] = [session];
}
});
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
TRYING TO USE THIS BELOW... session data is undefined, it can't access out of the scope?
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But this works..
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
So this new .map method can't access anything out of its scope.. sigh!
If anybody can help that would be amazing! Thanks!

In Javascript all functions can access variables from outside (called "higher scope") - it's one of the strengths of the language and is called "capture".
The reason your code is failing is because it's using this.sessionData inside a function declaration, which cases problems because this in javascript is... somewhat complex. But you don't need it!
You also need to make sure you return the value you want to output. Here's how I would write it:
this.sessionData = Object.keys(temp).map(key => temp[key]);

Related

Pass variable into function to be defined [duplicate]

This question already has answers here:
What's the difference between passing by reference vs. passing by value?
(18 answers)
Closed last year.
EDIT 2: Why has this very specific question been marked as a duplicate of this very conceptual one: What's the difference between passing by reference vs. passing by value?. If someone else had the same question as me, they wouldn't end up with this one on Google by searching for it and - even if they did - it wouldn't answer their question.
EDIT: Thanks everyone for your help so far. Pretty difficult stuff to understand from my point of view. The reason I'm doing this is that I've set up a function which I'm calling multiple times and in which I define a variable (a unique one with each call). I need to be able to refer back to each unique variable afterwards. Here is my actual attempted code below. What would the right way to do this be?
let newSeq1
let newSeq2
function sequenceClip(sample, length, sequenceVariable) {
let currentPosition = Tone.Transport.position
let whenToStart
if (currentPosition === '0:0:0') {
whenToStart = '0:0:0'
} else {
const barToStartOn = +currentPosition.slice(0, currentPosition.indexOf(':'))
whenToStart = `${barToStartOn + 1}:0:0`
}
sequenceVariable = new Tone.Sequence((time, note) => {
sampler.triggerAttackRelease(note, '4m', time)
}, [sample], length).start(whenToStart);
}
loop1.addEventListener('mousedown', () => {
sequenceClip("C3", '1m', newSeq1)
})
loop2.addEventListener('mousedown', () => {
sequenceClip("C#3", '4m', newSeq2)
})
How do I pass a variable into a function in Javascript to be assigned a value. E.g.:
Why does the variable not get assigned the value 5? And what's the way around this?
let a
function defineVariable(var2beDefined) {
var2beDefined = 5
}
defineVariable(a)
console.log(a === 5)
You would typically write an initializer function that returns a value and assign that return value to the relevant global variable. For example:
let newSeq1
let newSeq2
function sequenceClip(sample, length, sequenceVariable) {
let currentPosition = Tone.Transport.position
let whenToStart
if (currentPosition === '0:0:0') {
whenToStart = '0:0:0'
} else {
const barToStartOn = +currentPosition.slice(0, currentPosition.indexOf(':'))
whenToStart = `${barToStartOn + 1}:0:0`
}
return new Tone.Sequence((time, note) => {
sampler.triggerAttackRelease(note, '4m', time)
}, [sample], length).start(whenToStart);
}
loop1.addEventListener('mousedown', () => {
newSeq1 = sequenceClip("C3", '1m')
})
loop2.addEventListener('mousedown', () => {
newSeq2 = sequenceClip("C#3", '4m')
})
Note that both newSeq1 and newSeq2 will be undefined until the first mousedown/mouseup events.
Reassigning an identifier, by itself, never has any side-effects (in most circumstances) - the only change resulting from someIdentifier = someNewValue will be when other parts of the code reference that same someIdentifier.
If you want to pass in something to be assigned to, pass in a function which, when called, assigns to the outer variable.
let a;
function defineVariable(callback) {
callback(5);
}
defineVariable(newVal => a = newVal);
console.log(a === 5)
The only time that assigning to an identifier will have side effects is if:
Inside a function with a simple argument list, you reassign one of the parameters (in which case the arguments object will be changed as well)
You're using ES6 modules, and you reassign an identifier that's being exported, in which case other modules that import it will see the change as well

How to get returned values from my listener's callback ES6 way

I made an input that let me filter a table of softwares.
<input type="text" id="softwares-search" class="form-control" aria-label="Input de recherche" aria-describedby="softwares-search">
Then in javascript my filter work well if I console.log(....)
But when I replace it with a return, nothing is returned. I think it is due to my var affectation through the event listener :
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
return softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
var results = softwaresSearch.addEventListener('keyup', maxwell)
console.log(results);
}
Thank all
EDIT 1 :
I was so angry, so blind, I had S#!t in my eyes, no need to use a global :(
const softwaresSearch = document.getElementById('softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', (e) => {
search = document.getElementById('softwares-search').value;
var filtredSoftwares = softwares.filter(e => e.name.includes(search) || e.description.includes(search) );
renderTable(filtredSoftwares);
});
}
const renderTable = (softwares) => {
Object.values(softwares).forEach(value=>{
console.log(value);
});
// Todo build HTML table
}
Instead of returning I think you just need to replace the current array like this
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
softwares = softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
And results is not needed:
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', maxwell)
}
As far as I know, softwareSearch.addEventListener won't return anything, since that is an event listener, and does not return any value. It simply executes the function passed in the 2nd parameter. You could try doing this instead
softwaresSearch.addEventListener('keyup', () => {
var results = maxwell();
console.log(results);
});
What this would do is that, it would call your maxwell function when the keyup event, since that is what it looks you are trying to do.
Please share all relevant code before posting a question, this code includes the variable "softwares" that exist outside what is visible to us.
Additionally, there are some issues with your code.
I don't understand your naming of the maxwell function. You should name functions as verbs, not anything else. A function is a machine that is doing something, and possibly returning something. It should be named to what it is doing.
On the second line, you say "search = ... ", but you didn't declare it as a variable.
You are returning something based on a value that isn't validated ('search' can be either undefined or a string value in this case), hence, your return will most likely just return undefined and not any value at all.
Your function can possibly not return anything at all since you are returning something within your if-statement. You can use a closure to always return something.
I would also suggest passing a search string as a variable to your function that should return a list based on the search query. Getting in the habit of short, concise functions with expected inputs/outputs, will make your code more readable and less error-prone and less likely to produce unwanted side-effects.
I don't know the rest of your code, but I don't recommend assigning variables in the global scope. Your "maxwell", "softwareSearch" variables both exist in the global space, unless you have wrapped them in another function block already (such as jquerys $(document).ready(() => { ...everything here is scoped })
You are getting the same element in two different places in your code.
Here is an updated code sample, but I can't test it since I don't know the rest of your code.
/*
* Executing the whole thing in this IIFE will make all variables declared inside here scoped to this block only,
* thus they can't interfere with other code you may write
*/
(() => {
const listOfSoftwares = softwares; // --- get your softwares variable here somehow, I don't know where "software" comes from.
// input element
const search = document.querySelector('#softwares-search');
/**
* Filter search results
* #param {string} query Search query
* #returns {Array} The results array
*/
const filterSoftwareSearchResults = (query) => {
let results = [];
results = listOfSoftwares.filter(software => software.description.includes(query) || software.title.includes(query))
// Verify
console.log(results);
// Should return array of results, even if empty
return results;
}
if (search) {
search.addEventListener('keyup', () => {
filterSoftwareSearchResults(search.value)
})
}
})()
The addEventListener function always returns undefined, so your results variable is undefined.
Returning from the callback function (maxwell) is also of no use.
You either need to do something with the data inside of your callback, or maybe pass the data to a global variable.

Return value in JavaScript using variable name

I made a function in my JavaScript, here is the scene:
Please note, the code below is just for my scenario, its not working.
getData('bill', 'userAge');
Function getData(u, variable) {
fetch(`url`).then(r=> { return response.json();})
.then(u => {
var userFullName = u.fullname;
var userAge = u.age;
var userGender = u.gender
return variable
});
}
when I execute the function getData('bill', 'userAge') which userAge is the variable name inside the function. And the function will return the value from variable name: userAge inside the function, so I dont have to write another code like, if(variable=='userAge') return userAge;
Is it possible ? Just asking, because I have ton of variables in my function, for now I'm still using if(variable=='userAge') return userAge
Well, answering directly your question: No, if you want to return exactly with the variable you are passing right now, you'll need to map each variable name to each return property.
BUT, if you pass directly and exactly the name of the property as variable parameter, then you can just use return u[variable]. For Example, instead of passing userFullName, you pass just fullname.
I'm not going to enter in the merit that your current example code being completely wrong because Function ... is not going to work since you should use function ..., but here below I made a functional example using a different approach to the fetch using await and async, you can see that it's returning the property I specified, which is "title".
async function getData(variable) {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1', {});
const u = await response.json();
return u[variable];
}
getData('title').then(a => {console.log(a)});
To check if my example returned the expected property from the object, you can access the link: JSON Example
One solution:
getData('bill', 'age');
function getData(u, variable) {
return fetch(`url`).then(r=> { return r.json();})
.then(u => {
return u[variable];
});
}
If you must use different names in your variable parameters than in your u properties, you can map them rather than use if/switch:
paramToVariable = {
userAge: 'age',
...
};
...
return u[paramToVariable[variable]];
Or you may use a function to map rather than an object.

Passed-in Values Not Available at Run-time of Function in Angular App

I realize there is something I'm missing in terms of how and specifically when the products of certain functions are available in JavaScript.
In my Angular app, in order to get a user's initials, I am parsing data being returned from the API, and retrieving the first letter of the firstName, as well as the first letter of lastName in two different functions. These two functions are working as expected, and I can see the correct results in the console:
getFirstNameFirstLetter() {
if (this.authenticationService.isAuthenticated()) {
const userObj = JSON.parse(sessionStorage.getItem('currentUser'));
const userInfo = userObj.data;
const firstName = userInfo.name.first;
const firstNameFirstLetter = firstName.trim().charAt(0);
console.log(firstNameFirstLetter);
return firstNameFirstLetter;
}
}
getLastNameFirstLetter() {
if (this.authenticationService.isAuthenticated()) {
const userObj = JSON.parse(sessionStorage.getItem('currentUser'));
const userInfo = userObj.data;
const lastName = userInfo.name.last;
const lastNameFirstLetter = lastName.trim().charAt(0);
console.log(lastNameFirstLetter);
return lastNameFirstLetter;
}
}
Now comes the part I'm not fully understanding. When I then pass the returned values of these two functions, in order to get the initials, like this:
getInitials(firstNameFirstLetter, lastNameFirstLetter) {
if (this.authenticationService.isAuthenticated()) {
if (!this.firstNameFirstLetter || !this.lastNameFirstLetter) {
console.log('Names not ready!');
return;
} else if (this.firstNameFirstLetter && this.lastNameFirstLetter) {
console.log(firstNameFirstLetter + lastNameFirstLetter);
return firstNameFirstLetter + lastNameFirstLetter;
}
}
}
... I get "Names not ready!" printed to the console each time.
By the way, I am running these functions within Angular's ngOnInit life cycle hook, like this:
ngOnInit() {
this.getFirstNameFirstLetter();
this.getLastNameFirstLetter();
this.getInitials(this.firstNameFirstLetter, this.lastNameFirstLetter);
}
I know this has something to do with what's available when, because I get 'undefined' when I use break points and debug the two values being passed into the "getInitials()" function. In other words, the function doesn't have access to the returned values of the other two functions at the time it's run -- hence I'm getting 'Names not ready!' printed to the console. My question is, what am I missing, architecturally, to resolve this kind of issue?
So what is happening here is that JavaScript doesn't think you are using the return values for getFirstNameFirstLetter and getLastNameFirstLetter, so when it makes the call, instead of waiting for that call to finish, it goes on to the next one, which introduces a race condition. if you simply change it to
ngOnInit() {
let temp1 = this.getFirstNameFirstLetter();
let temp2 = this.getLastNameFirstLetter();
this.getInitials(this.firstNameFirstLetter, this.lastNameFirstLetter);
}
then it will wait for the previous functions to finish before calling the next.
Also, I don't use const very often, so I could be wrong and it could follow different scope rules, but by normal scope rules, setting a variable in that function, it is only available in that function, you would need to set it as
this.firstNameFirstLetter = firstName.trim().charAt(0);
to have access to it outside the function.
Or, so as to kill two birds with one stone, you could do
ngOnInit() {
this.firstNameFirstLetter = this.getFirstNameFirstLetter();
this.lastNameFirstLetter = this.getLastNameFirstLetter();
this.getInitials(this.firstNameFirstLetter, this.lastNameFirstLetter);
}
or
ngOnInit() {
let firstNameFirstLetter = this.getFirstNameFirstLetter();
let lastNameFirstLetter = this.getLastNameFirstLetter();
this.getInitials(firstNameFirstLetter, lastNameFirstLetter);
}
depending on if you need the variables again or just for that function.

How to reuse array functions in an infinite loop

The real code is larger, so I won't post it. It looks pretty much like this:
class A {
process(source) {
// I perform several operations with array helper functions here:
const filtered = source.filter(item => item);
const condition = filtered.some(item => item);
if (condition) {
const mapped = source.map(item => /* Mapping operations... */);
const sorted = mapped.sort((a, b) => { /* Some sort conditions... */ });
return sorted;
} else {
const mapped2 = filtered.map(item => /* A different mapping operation... */);
return mapped2;
}
}
}
const a = new A();
while (true) {
const source = getSourceFromSomewhere(); // Array (40 - 50 items aprox)
const b = a.process(source);
// ...
}
The problem: Basically, performance; "Don't make functions within a loop".
On every iteration a bunch of anonymous functions are getting created.
My solution:
class A {
// Predefine it:
sort() { /* Sort logic */ }
map() { /* Map logic */ }
map2() { /* Map logic */ }
filter() { /* Filter logic */ }
some() { /* Condition */ }
process(source) {
const filtered = source.filter(this.filter); // Note: Scope of 'this' is changed.
const condition = filtered.some(this.some);
if (condition) {
const mapped = source.map(this.map);
const sorted = mapped.sort(this.sort);
return sorted;
} else {
const mapped2 = filtered.map(this.map2);
return mapped2;
}
}
}
Another problem: Some of this functions need access to properties of the object itself, but the scope of this has been changed.
It's worth to call .bind(this) instead of creating the anonymous function? or pretty much the same?
What would you do in my case?
Thanks in advance.
To initialize bound functions within a class you could do
class Test {
fn = (t) => this[t]
}
basically the same what you wanted to do anyways.
The problem: Basically, performance; "Don't make functions within a loop".
Your premise is incorrect.
JavaScript engines are highly optimized. They do not laboriously read the source text character-by-character each time through a loop, or each time a function is called, much less each time a callback is invoked. They scan, parse, and pre-compile. At worst, functions like item => item will be created only once per function invocation. More likely, they will be pre-created during the initial scanning and parsing process.
Therefore, you don't need to worry about performance when considering whether to pre-define the functions yourself. The guiding principle should instead be program readability and structure.
If you do want to pre-define a function, as long as it does not use this, consider defining it outside the class:
function filterFunc(item) { return item.val < MAX; }
class A {
process() {
const filtered = source.filter(filterFunc);
If you do need 'this`, then in modern JS it is preferable to write
class A {
filterFunc(item) { return item.val < this.MAX; }
process() {
const filtered = source.filter(item => this.filterFunc(item));
instead of worrying about binding this.filterFunc making you write
class A {
constructor () { this.filterFunc = this.filterFunc.bind(this); }
process() {
const filtered = source.filter(this.filterFunc);
While as mentioned in another answer
class Test {
// constructor etc.
step = x => x + this.currentStep;
process() {
return this.arr.map(step);
}
}
would be a concise way to achieve your intended behavior, as this is already bound to the instance, it requires public class fields which is still in Stage 2, and therefore not yet supported in many browsers without a transpiler.
It is good to remember that you can always pass the this scope to the second argument of functions such as map and filter, so you don't have to manually bind your functions beforehand. The code then becomes
class Test {
// constructor etc.
step(x) { return x + this.currentStep; }
process() {
return this.arr.map(step, this);
}
}
This is very close to the solution you have in mind while making sure your functions have the correct scope.
Though I don't know much about inner workings of browsers I think if the code is hot enough (that is being ran often), the optimized compiler might not need to recreate those anonymous functions every run.

Categories

Resources