Conditionally traverse an Array - javascript

I writing code that should mimic the functionality of Underscores's _.each method. However, with the code I have now...
var testArr = ['a','b','c'];
var eachFunc = function(collection, iterator) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.legnth; i++ ) {
iterator(collection[i]);
}
}
else {
for ( var property in collection ) {
iterator(collection[property]);
}
}
};
console.log(eachFunc(testArr, console.log));
I only return undefined. I would expect to log 'a','b' then 'c' to the console. I have verified that Array.isArray(testArr) is returning true and that the if block is entered into. It's the for loop that I'm not entering into properly.
Can someone please tell me what I'm doing wrong with this for loop?

1) Typo: legnth.
2) You can't pass console.log directly - it results in Illegal invocation error. Pass either console.log.bind(console) or anonymous function.
3) That undefined is returned value of your function.

Related

Using callback's returned value as an argument to another function

I have referenced multiple SO questions but I still could not find a solution. These are the questions I took a look at (main ones):
Pass a JavaScript function as parameter
How to execute a method passed as parameter to function
compute.js:
const mainTable = document.getElementById('nonFixedSample');
function getRows(metricName) {
let row = 0;
let z = document.getElementsByTagName('tr');
for (let i = 0; i < z.length; i++) {
if (mainTable.rows[i].firstChild.textContent === metricName) {
row = i;
return row;
}
}
}
// Here I am trying to pass that function as callback
function stdCellArea(callback) {
rowNumber = callback();
let runs = mainTable.rows[rowNumber].cells.length;
// Other code
}
Now I am calling it, reg_report.php:
<script>
stdCellArea(function() {
getRows('test');
});
</script>
But I get the following error:
Uncaught TypeError: Cannot read property 'cells' of undefined
at stdCellArea (compute.js:17)
at reg_report.php:39
Basically, I need to use return value of getRows() as an argument for stdCellArea(). I know I could simply do this:
let x = getRows('text');
stdCellArea(x);
But I have to call this function over 10 times, so I do not want to create many variables. Who can help?
You need to return the value from your callback: return getRows('test');. Without that, rowNumber becomes undefined as that's what functions without an explicit return, return.

Get object out of observable array

Why is m "undefined" in this code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for(var i=0;i<currentViewModel.availableReports().length;i++) {
if(currentViewModel.availableReports()[i].id == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
I call getReport() as an onclick event and I want to send the report object to a view (modal) I can do a foreach on the availableReports and it's all there. When I run through the debugger, it loops through the array and finds the right one. But why can't I pull it out of the array? "m" remains undefined the the function returns undefined.
What am I missing here?
EDIT: there is a follow up question here:
Can knockout.js wait to bind until an onClick?
You just need to change if(currentViewModel.availableReports()[i].id ... to if(currentViewModel.availableReports()[i].id() ... because after mapping id will become an observable, i.e. function.
Updated code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id() == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
Demo - Fiddle.
I'll repeat the solution from #NikolayErmakov's answer here, but want to add two things to get a more complete answer. You end with:
...m remains undefined and the function returns undefined.
What am I missing here?
You're missing two things:
The var m bit of the first statement inside the if is hoisted to the top of the current scope (the top of the function). This is why the debugger can tell you what m is, even if you never reach the line of code it's on.
If a function invocation reaches the end of a function (as is the case for you, since you never go inside the if) without seeing an explicit return statement, it will return undefined.
To better understand this, you should interpret your function like this:
currentViewModel.getReport = function(reportId) {
var m;
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id == reportId) {
m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
return undefined;
}
Some people (e.g. Douglas Crockford) do recommend placing var statements at the top of a function, though it's a matter of style to some degree. I don't think many people explicitly return undefined at the end of a function, though in your case I might be explicit about that scenario and return null (or throw an Error even).
As promised, I'll repeat the actual solution, as I concur with the other answer:
you need to invoke id as a function to get its value (because the mapping plugin will map to observable()s.
In addition:
I'd retrieve the array only once
I'd suggest using === instead of ==
Here's my v0.5 version:
currentViewModel.getReport = function(reportId) {
var m = null, reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
m = reports[i];
return m;
}
}
return m;
}
But I'd optimize it to this v1.0:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
return reports[i];
}
}
return null;
}
For completeness, here's another version that utilizes filter on arrays:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports().filter(function(r) { return r.id() === reportId; });
return reports.length >= 1 ? reports[0] : null;
}

Returning an array containing named properties of each object

Just getting stumped and not sure why my code isn't working. The instructions are to take an array of objects and a property name, and return an array containing the named property of each object.
so something likepluck([{a:1}, {a:2}], 'a') // -> [1,2] where pluck is the function I want to create.
So far, I have:
function pluck(objs, name) {
var pushedArray=[];
for (i=0;i<objs.length;i++){
var totalpushedArray = pushedArray.push(name[i]);
}
}
but the code itself isn't working as far as I can tell. There are additional guidelines to leave undefined if the object doesnt have the property but I figure that I will get to that after I solve this first.
You forgot to add a return statement and you're not referencing the object property. See below.
function pluck(objs, name) {
var pushedArray = [];
for (var i = 0; i < objs.length; i++) {
pushedArray.push(objs[i][name]);
}
return pushedArray;
};
If you want a more "functional" solution, you can use map.
function pluck(objs, name) {
return objs.map(function(obj) {
return (obj.hasOwnProperty(name) ? obj[name] : null);
});
};

Possible to ignore Cannot read property '0' of undefined? [duplicate]

This question already has answers here:
How to avoid 'cannot read property of undefined' errors?
(18 answers)
Closed 2 years ago.
I am creating a personal script that in some instances gets the error:
Cannot read property '0' of undefined
I have something like this
item["OfferSummary"][0]["LowestUsedPrice"][0]["FormattedPrice"]
Is it possible to completely ignore/override this error so that it just prints n/a or -- in that scenario?
You can use try and catch to perform error handling.
You can use a boilerplate function to do so:
function get(obj, property) {
if (Array.isArray(property)) {
var current = obj;
for (var i = 0, l = property.length; i < l; ++i) {
if (Object(current) === current) current = current[property[i]];
else {
current = undefined;
break;
}
}
return current;
}
if (Object(obj) === obj) return obj[property];
}
Pass either a string or an array to get to find the property -- if not found, undefined will be returned.
Example:
get(window, ['location', 'href']); // "http://stackoverflow.com..."
get(Number, 'MAX_VALUE'); // 1.7976931348623157e+308
Even if you can use try and catch I wouldn't do that, I prefer avoid errors at all, so you'd just need to check the object you're reading:
if(item && item["OfferSummary"].length && item["OfferSummary"][0]["LowestUsedPrice"].length) {
//then do whatever
}
if you know that item is always defined you can avoid to check it in the if.
Similar to Qantas' answer, but using an in test. Always expects the property list to be an array, I can't see the point of using this to get a single property so no concession for that case:
function get2(obj, prop) {
for (var i=0, iLen=prop.length - 1; i<iLen; i++) {
if (typeof obj[prop[i]] == 'object') {
obj = obj[prop[i]];
} else {
// Property not found, return undefined (or other suitable value)
return;
}
}
return obj[prop[i]];
}
var foo = {foo:{bar:{meh:'meh!'}}};
var fum = {meh:'meh!'};
console.log(get2(foo,['foo','bar','meh'])); // meh!
console.log(get2(fum,['meh'])); // meh!
console.log(get2(Number,['MAX_VALUE'])); // 1.7976931348623157e+308
console.log(get2(Object,['prototype','toString'])); // function toString() { ... }
Edit
Per Qantas' comment, the test has been updated.

How do I check if a property exists in an object/dictionary?

I'm iterating over an array of words and trying to stuff them in an object literal so I can assign the value of how many times those words occur to each word in the literal/dictionary. The problem is I need to check to make sure that word hasn't already been added into my literal. I tried using in to check if the property exists in the literal but it's throwing an error:
Cannot use 'in' operator to search for 'We' in undefined
Here's problematic function:
I commented the line that's causing the problem
function wordCountDict(filename) {
wordCount = {};
inputFile = fs.readFile( root + filename, 'utf8', function( error, data ) {
if(error) {
console.log('error: ', error)
return false;
}
var words = data.split(" ");
for (i in words) {
if(words[i] in wordCount) { // This is where the problem occurs
wordCount[words[i]]++;
} else {
wordCount[words[i]] = 1;
}
console.log(words[i]);
}
});
}
I'm coming from python and this was always the best/easiest way to achieve this, but javascript doesn't seem to agree.
How would I do this in JavaScript?
Declare wordCount as a local variable to that function. It is probably getting overwritten elsewhere:
function wordCountDict(filename) {
var wordCount = {};
...
}
This is a bad idea
for (i in words) {
do not use a for loop to loop through an array! If something is added to the array prototype it will be checked.
var words = data.split(" ");
for (var i=0; i<words.length; i++) {
if(words[i] in wordCount) {
Next thing, is readFile is asynchronous. If code outide of it resets wordCount to an undefined value, you can get this error. You are better off using a local variable and setting the global value when the looping is done. Also that return false does NOTHING inside the readFile.
function wordCountDict(filename) {
var tempWordCount = {};
var inputFile = fs.readFile( root + filename, 'utf8', function( error, data ) {
if(error) {
console.log('error: ', error)
return false;
}
var words = data.split(" ");
for (var i = 0; i<words.length; i++) {
if(words[i] in wordCount) { // This is where the problem occurs
wordCount[words[i]]++;
} else {
wordCount[words[i]] = 1;
}
console.log(words[i]);
}
wordCount = tempWordCount; //set the global variable equal to the local value
});
}
If all you would like to do is check for existance in the object, you can use this:
if(typeof wordCount[words[i]] === 'undefined'){
...
}
I would not recommend just using if(wordCount[words[i]]) because technically there could be a property of the object that exists but evaluates to false.
Note that in Javascript doing something like myObject.something is equivalent to myObject['something'] on an object, and that when you use myObject['somethingElse'] you are basically just dynamically adding members to the object. In Javascript, objects can be used like Python dictionaries, but they really aren't the same thing.

Categories

Resources