I have little experience with JS and I'm following a video course on React. Most things makes sense but I'm having some trouble with reference scopes.
Here's what the tutor writes:
const orderIds = Object.keys(this.props.order);
const total = orderIds.reduce((prevTotal, key) => {
const fish = this.props.fishes[key];
const count = this.props.order[key];
const isAvailable = fish && fish.status === 'available';
if (isAvailable === true) {
return prevTotal + count * fish.price;
} else {
return prevTotal;
}
}, 0);
He is using an arrow-function. It works of course.
I want to do the same but with writing a "normal" function, because for now I don't want to make any shortcuts:
const orderIds = Object.keys(this.props.order);
const total = orderIds.reduce(
function(prevTotal, key) {
const fish = this.props.fishes[key];
const count = this.props.order[key];
const isAvailable = fish && fish.status === 'available';
if (isAvailable === true) {
return prevTotal + (count * fish.price);
} else {
return prevTotal;
}
}
, 0);
The problem is:
TypeError: this is undefined
So there's a problem with the scope of the reference to this.props...
I got this to work within a .map()-function by passing it a second argument of
... , this)
from searching online about referencing this.
That doesn't work for the reduce()-function. So, what can one do in my situation? Again, I don't want to use the shorter arrow-function because it's like a shortcut I don't want to use when learning. I've searched online about scopes on this and also about the reduce()-function but the examples I've seen doesn't quite fit what I'm looking for, or I'm simply not savvy enough to use what they're writing.
Thanks a ton if you can help me out, regards / HW
Arrow functions has parent scope so this represents parent's this but simple function don't. You can tell the function about this using .bind(this)
const total = orderIds.reduce(
(function(prevTotal, key) {
const fish = this.props.fishes[key];
const count = this.props.order[key];
const isAvailable = fish && fish.status === 'available';
if (isAvailable === true) {
return prevTotal + (count * fish.price);
} else {
return prevTotal;
}
}).bind(this)
, 0);
You can use bind as suggest upper or rewrite code like this:
const orderIds = Object.keys(this.props.order);
const { fishes, order } = this.props;
const total = orderIds.reduce(
function(total, key) {
const fish = fishes[key];
const count = order[key];
const isAvailable = fish && fish.status === 'available';
if (!isAvailable) return total;
return total + (count * fish.price);
}
, 0);
Related
I am trying to get the original sorted array in the else statement
My code is something like this,
const sortItems = (data) => {
let priceArray = [];
// Making an new array
// let originalArray = productList.slice();
let originalArray = [...productList]
if (filteredState === true) {
priceArray = filteredInfos;
} else {
priceArray = [...productList];
}
if (data === 'Price low - high') {
priceArray.sort(
(a, b) => a.discountedPriceWithDouble - b.discountedPriceWithDouble,
);
} else if (data === 'Price high - low') {
priceArray.sort(
(a, b) => b.discountedPriceWithDouble - a.discountedPriceWithDouble,
);
} else if (data === 'Newest') {
// priceArray = originalArray.slice();
priceArray.sort(
(a, b) => new Date(b.createdDate) - new Date(a.createdDate),
);
} else {
priceArray = originalArray;
}
setDupFilteredList(priceArray);
setFilteredInfos(priceArray);
setFilterModalSevenVisible(false);
setOpenFilterList(false);
};
When reading other answers from stack overflow they suggest to create a new array before sorting the original array.
So I create a new array by this method let originalArray = [...productList]
But the else statement is not working, Which I intended to return to the original unsorted state. What am I doing wrong here?
Your problem is scope.
If a variable is declared with let, it is a local variable (can't be used outside of the brackets or in new brackets.
Use either const or var to declare the variable and use it outside of the local scope.
If this doesn't work:
Why not make two variables when declaring originalArray? If you make a duplicate with a different name, you can refer to it as your 'original list.' Here is what I'd do:
Outside of the const func
var original = [];
Inside of the const func
let originalArray = [...productList]
original = originalArray;
Inside of the else portion
priceArray = original;
If this doesn't work, I don't understand the question from your description.
When I try to grab the object from the array, the type is undefined. Therefore I cannot use a method from the undefined object as it doesn't exist. I am relatively new to JavaScript and I have come straight from Java so the way of retrieving objects is kind of new to me. This is what I currently have.
var fleetAmount = 0;
var fleets = [];
function Fleet(number) {
this.number = number;
this.activities = [];
this.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(this);
}
var getFleet = function(fleetNumber) {
return fleets[fleetAmount - fleetNumber];
}
This is where I try to grab the object and preform the function
const Fl = require(‘fleet.js’);
const fleet = Fl.getFleet(fleetNumber);
fleet.addActivity(activity);
I am also working in Node.js, which is how I am using the require method.
In combination with the answer from #audzzy I changed the getFleet() function so that it would be more efficient. I tested it out and it worked. This is what I used
function getFleet(fleetNumber) {
let result = fleets.filter(function (e) {
return e.number = fleetNumber;
})
return result[0];
}
Thanks for the help! I appreciate it.
you want to create a new fleet object and add it, not "this"..
adding "this" would cause a circular reference, where
this.fleets[i] = this (and all fleets would have the same value)
when calling get fleet, I would check that a fleet was returned from get fleet
in case amount is less than the number you send to getFleet (where according to what you posted: 1 returns the last, 2 returns second to last etc..)..
I hope this explanation makes sense.. anyways, this should work..
var fleets = [];
doStuff();
function doStuff(){
addFleet(1);
addFleet(2);
addFleet(7);
addFleet(3);
// should return null
let fleet1 = getFleetByNumber(5);
// should return the fleet with number 7, and not change the fleet with number 1
let fleet2 = getFleetByNumber(7);
if(fleet2){
fleet2.addActivity("activity");
}
console.log(`fleets: ${JSON.stringify(fleets)} \nfleet1: ${JSON.stringify(fleet1)} \nfleet2: ${JSON.stringify(fleet2)}`);
}
function addFleet(number) {
let fleet = { number: number,
activities: [] };
fleet.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(fleet);
}
function getFleetByNumber(fleetNumber) {
return fleets.find(function (e) {
return e.number == fleetNumber;
});
}
function getFleet(fleetNumber) {
let result = null;
if(fleets.length - fleetNumber >= 0){
result = fleets[fleets.length - fleetNumber];
}
return result;
}
I have a code block like below where I need to find something inside a loop, and also return a second variable. So I can't use a simple Array.find or Array.some good ole' for...of is my friend. map/filter don't allow a break and find can only return actual elements from the array, not a related calculation.
But the below within typescript is giving me an unavoidable error.
I'm wondering if either there's a more idiomatic way to do this, or a better structure / place to declare the variable?
Variable 'found' is used before being assigned.
let found: ParseResult
// breaks when first item found but we capture a different value
for (const rule of ParserRules) {
// const rex = new RegExp(route.match)
const parsed = rule.rex.exec(input)
if (parsed) {
found = { parsed, rule }
break
}
}
// #ts-ignore
return found // FIXME used before defined?
Here are the various JS iterator methods I tried...
const ar = [1, 2, 3, 4]
const log = console.log
const finder = (list) => {
console.log('map', list.map(it => it === 3))
console.log('find', list.find(it => it === 3))
console.log('some', list.some(it => it === 3))
console.log('filter', list.filter(it => it === 3))
console.log('find', list.find(it => {
if (it === 3) return it * 2 // value coerced to T|F
}))
console.log('filter', list.filter(it => {
if (it === 3) return it * 2 // value coerced to T|F
}))
const arr = list.forEach((k) => {
if (k === 3) return ('here')
})
log('arr', arr)
let found
for (const elem of list) {
log('elem of', elem)
if (elem === 2) {
found = elem
break
}
}
log('found', found)
}
finder(ar)
The summary of your problem is that a function returning a value in a variable when, at times, the logic doesn't get a chance to assign any value to it.
You can either initialize the variable with a default value or, at the point of returning, check if it really has a value.
let found: ParseResult= {}
OR
return found || false //Or an empty object etc
This can be done in many ways but what might suit your case would be
ar
.map((rule) => {
const parsed = rule.rex.exec(input)
if (parsed) {
return { parsed, rule }
}
})
.find((x) => !!x)
Yes you are looping it once more but this is more readable. Also it would not be that costly.
If your processing is heavy, you can try this approach as well but this will be a custom implementation and will not come out of the box:
function getParsedValue(ar, input) {
let parsed;
const rule = ar
.find((rule) => {
parsed = rule.rex.exec(input);
return !!parsed;
})
return !!rule ? { rule, parsed } : null
}
I'm developing a simple Javascript Client API for my unit tests, as I'm studying TDD and learning better by doing things.
I'm following a model where they will only be a CHECK by test, I have my TEST function, which is where all the tests of a given file will be, and each test will be called by TEST_F with only one CHECK function each, thus not needing a description for each TEST_F, since with just a CHECK, it is simple and easy to understand with just a good "nameTest".
The problem I'm having is to use the Javascript spread, I actually know well how to solve my problem without it, but I would like to understand if it could help me simplify things here. The TEST function, as I said, might get several TEST_F functions as arguments, so I thought I'd do something like const TEST = (... f) => {};, but I'm not sure how to use each "f" argument since each TEST_F function returns me an object, which I want to use to accuse the TEST_Fs that fail. I will try to explain what I try to do with the code below that we know will not work, but only to understand where I am trying to get:
/* --------------------------------------------------------------- */
/* ------------------- Test API ---------------------------------- */
/* --------------------------------------------------------------- */
const TEST = (fileName, ...f) => {
const passing = [];
const failing = [];
console.log('Running unit tests in '+fileName+':');
const tStart = performance.now();
const result = ...f(); // I know it's not possible, but you understand what I'm trying to do?
result.resultTest==='passed'?passing.push(result):failing.push(result);
const tEnd = performance.now();
const duration = tEnd - tStart;
const lenPassing = passing.length;
const lenFailing = failing.length;
console.log('Passing: '+lenPassing+' ('+duration+'ms)');
console.log('Failing: '+lenFailing);
if(lenFailing > 0){
let stg = '';
for(let i = 0; i < lenFailing; i++){
stg += (i + ') ' + failing[i].nameTest + ' (' + failing[i].durationTest + ')' + '\n');
}
console.log(stg);
}
};
const TEST_F = (nameTest, f) => {
const tStart = performance.now();
const resultTest = f();
const tEnd = performance.now();
const durationTest = tEnd - tStart;
return { nameTest: nameTest, durationTest: durationTest, resultTest: resultTest };
};
const CHECK_EQUAL = (value, expected) => {
return ((value === expected)?'passed':'failed');
};
export {
TEST,
TEST_F,
CHECK_EQUAL
};
Up 1:
How would I solve my problem without using the spread? creating a TEST object that contains an array of TEST_F and then would create a function to run the tests, something like EXECUTE_TEST, but what I want to avoid is having to call a function always in my test files, I want something simple like:
TEST("main.js",
TEST_F("test1", () => {
return CHECK_EQUAL(3, 3);
}),
TEST_F("test2", () => {
return CHECK_EQUAL(7, 3);
})
);
which I was able to solve with the #TJ Crowder answer:
for(let fn of f) {
const result = fn;
result.resultTest==='passed'?passing.push(result):failing.push(result);
}
If your goal is to call each function in the f array and get the results from them into the passing or failing arrays, I'd probably use a simple for-of:
for (const fn of f) {
const result = fn();
result.resultTest==='passed'?passing.push(result):failing.push(result);
}
or forEach or similar, any of the array looping mechanisms would do the job.
Okay, I realize this can be considered subjective, but I'm trying to better understand how to consider scope when writing modules that only expose what's needed publicly. I have a string utility that I've written as an object literal below:
const substrings = {
query: {},
text: "",
results: [],
exists: function (index) {
const exists = index >= 0
return exists
},
check: function () {
const q = this.query
const start = q.openIndex
const stop = q.closeIndex
if (q.hasOpen && !q.hasClose) {
console.log("Missing closing delimiter.")
}
if (!q.hasOpen && q.hasClose) {
console.log("Missing opening delimiter.")
}
if (q.hasOpen && q.hasClose && start > stop) {
console.log("Closing delimiter found before opening.")
}
if (!q.hasOpen && !q.hasClose && this.results.length == 0) {
console.log("No results found.")
}
const order = start < stop
const check = q.hasOpen && q.hasClose && order
return check
},
update: function () {
const q = this.query
const text = this.text
q.before = this.text.indexOf(q.open)
q.start = q.before + q.open.length
this.text = text.slice(q.start, text.length)
q.stop = this.text.indexOf(q.close)
q.after = q.stop + q.close.length
q.openIndex = q.before
q.closeIndex = q.before + q.stop
q.hasOpen = this.exists(q.openIndex)
q.hasClose = this.exists(q.stop)
const newPosition = q.start + q.after
q.position = q.position + newPosition
this.query = q
},
substrings: function () {
const q = this.query
const current = this.text.slice(0, q.stop)
const fullLength = this.text.length
this.text = this.text.slice(q.after, fullLength)
this.results.push(current)
this.update()
if (this.check()) {
this.substrings()
}
},
init: function (open, close, text) {
this.results = []
this.query = {
open,
close,
position: 0,
}
this.text = text
this.update()
},
getSubstrings: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
this.substrings()
return this.results
}
},
getSubstring: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
return this.text.slice(0, this.query.stop)
}
}
}
I want to use it as a Node module and expose the getSubstring and getSubstrings methods, but if I were to do:
module.exports = {
all: substrings.getSubstrings,
one: substrings.getSubstring
}
I would get an error due to the usage of this. I realize that if I replace this with the object var name substrings to reference it directly, it works. I could also refactor it to be one big function or smaller functions and just export the 2 I need.
I am trying to go about learning things the right way and am struggling with how I should be thinking about context. I understand how this changes here, but I feel like I'm not fully wrapping my head around how I should consider context when structuring my code.
Is there a more elegant solution to expose methods with code like this that wasn't written to separate private and public methods?
A simple solution would be to bind the exported functions to the proper calling context inside the exports object:
module.exports = {
all: substrings.getSubstrings.bind(substrings),
one: substrings.getSubstring.bind(substrings)
}
Personally, I prefer using the revealing module pattern over object literals for situations like this. With the revealing module pattern, create an IIFE that returns the desired functions, referring to local variables instead of properties on this. For example:
const { getSubstrings, getSubstring } = (() => {
let query = {}
let text = ''
let results = []
function exists(index) {
return index >= 0
}
function check() {
const q = query;
// ...
}
...
function getSubstrings(open, close, text) {
}
...
return { getSubstrings, getSubstring };
})();
module.exports = {
all: getSubstrings,
one: getSubstring
}
This is somewhat opinion-based, but code can be easier to read when there aren't any this references to worry about.