I'm rewriting my old table sort library. In the library, I have this method which gets some attributes from the table:
<table data-sort-cols="" data-sort-orders="">
getSortInfo : function(element) {
switch (element.tagName) {
case "TABLE":
function getSortAttr(element, attr) {
var info = element.getAttribute(attr);
if (info != "") {
info = info.split(",").map(function(val) { return parseInt(val,10) });
} else {
info = [];
}
return info;
};
return {
columns : getSortAttr(element, "data-sort-cols"),
orders : getSortAttr(element, "data-sort-orders"),
}
I want to shorten the getSortAttr function. Function returns the assigned value (e.g. "1,3,2" -> [1,3,2]). If there is not an assigned value to the attribute, it returns an empty array. I want to get rid of the if statement.
Is there a way to modify this line, and make it return an empty array if the string is empty ""?
info = info.split(",").map(function(val) { return parseInt(val,10) });
I've tried
// returns [NaN]
"".split(",").map(function(val) {
return parseInt(val, 10);
})
// returns [undefined]
"".split(",").map(function(val) {
var num = parseInt(val, 10);
if (!isNaN(num) && isFinite(num)) {
return num;
}
})
but didn't work.
The following function will check if info == "", if info == "", it will then return an empty array, if not, it will split the info string and map it. This is called a shorthand if statement.
info = (info == "") ? [] : info.split(",").map(function(val) { return parseInt(val,10) });
You can also add more conditions to the shorthand condition, e.g:
(info == "" || info != undefined || info != null)
If the above info == "" doesn't work, this might mean that info is undefined. To combat this we can use the following statement:
(typeof(info) != undefined)
You can use
function getSortAttr(element, attr) {
return (element.getAttribute(attr) || "").split(",").filter(Boolean).map(Number);
}
to filter out empty strings from the array before mapping them to numbers.
Related
I'm populating a table with data - using fixed-data-table, which is a React.js component. However, that isn't so important at this stage.
The table has a search box where the issue stems from.
First, here's the interesting part of the code.
for (var index = 0; index < size; index++) {
if (!filterBy || filterBy == undefined) {
filteredIndexes.push(index);
}
else {
var backendInfo = this._dataList[index];
var userListMap = hostInfo.userList;
var userListArr = Object.values(userListMap);
function checkUsers(){
for (var key in userListArr) {
if (userListArr.hasOwnProperty(key) && userListArr[key].text.toLowerCase().indexOf(filterBy) !== -1) {
return true;
}
}
return false;
}
if (backendInfo.firstName.indexOf(filterBy) !== -1 || backendInfo.lastName.toLowerCase().indexOf(filterBy) !== -1 || backendInfo.countryOrigin.toLowerCase().indexOf(filterBy) !== -1
|| backendInfo.userListMap.indexOf(filterBy) !== -1) {
filteredIndexes.push(index);
}
}
}
This is rendered and the last part is throwing errors if you input something in the table, and a column returns null from the user input.
The thing is, I can make the code work if I change the last part to ..
try {
if (backendInfo.firstName.indexOf(filterBy) !== -1 || backendInfo.lastName.toLowerCase().indexOf(filterBy) !== -1 || backendInfo.countryOrigin.toLowerCase().indexOf(filterBy) !== -1
|| backendInfo.userListMap.indexOf(filterBy) !== -1) {
filteredIndexes.push(index);
}
}
catch(err) {
console.log('Exception')
}
With the try/catch, it works 100% as intended and handles the indexOf returning null... But this can't be the way to properly handle it - I'm assuming this sort of exception handling is, well, supposed to be for rare exceptions, and shouldn't really be used on the front-end as much as the backend.
How do I handle indexOf returning null in the above Javascript code? It might return null in any of the sources columns that are being populated.
If a key cannot be found, JS will throw an error. Try-catch is a good way to fix these errors, but there is an alternative:
You could check if keys exist in an object prior to pushing a value into it.
var data = { };
var key = "test";
// your method works great
try {
var value = data.firstname.indexOf(key);
} catch (err) {}
// another method, I'd prefer the try/catch
var value = data.firstname ? data.firstname.indexOf(key) : undefined;
// test if the object is the type of object you are looking for
// this is in my opinion the best option.
if(data.firstname instanceof Array){
var value = data.firstname.indexOf(key);
}
// you can use the last option in your code like this:
var firstnameHasKey = data.firstname instanceof Array && ~data.firstname.indexOf(key);
var lastnameHasKey = data.lastname instanceof Array && ~data.lastname.indexOf(key);
if(firstnameHasKey || lastnameHasKey){
// logics
}
If you test the instanceof && indexOf, there will never be an error. If firstname is undefined, the indexOf will never be checked.
Ofcourse you can use this for other types:
var myDate = new Date();
myDate instanceof Date; // returns true
myDate instanceof Object; // returns true
myDate instanceof String; // returns false
MDN documentation
I have a Javascript array with multiple values:
var filterClasses = ['col-sm-12', 'hidden-xs', 'hidden-sm', 'hidden-lg', 'hidden-md', 'active', 'btn-'];
I have a function that gets all css classes in the DOM. But i want to check if this class should be added to a new array or not. So i can use indexOf for this:
return filterClasses.indexOf('col-sm-12');
This returns a true, so this class should be ignored.
But now i have a class that is btn-primary. As you see in my array i have the btn- added in it. I want to exclude all classes that contains the word btn-. How can i achieve this?
Current function:
function setupShouldAddClass( cssClass, filterClasses )
{
// If the cssClass exists in the filterClasses then false
if ( filterClasses.indexOf(cssClass) > 0 )
{
return true;
}
filterClasses.forEach(function ( item )
{
if ( stringContains(item, cssClass) )
{
return true;
}
});
return false;
}
function stringContains( needle, haystack )
{
return (haystack.indexOf(needle) !== -1);
}
Maybe you can solve your issue using regular expressions instead of using imperative code:
var classBlackListRegExp = /(col-sm-12|hidden-xs|hidden-sm|hidden-lg|hidden-md|active|^btn-.+)/i;
var result = classBlackListRegExp.test("btn-whatever");
console.log(result);
Check the ^btn-.+ part. This matches anything starting with "btn-".
I believe that your scenario is the ideal use case of regular expressions!
OP concerns if class black list is very large
OP said:
what im wondering is, that if i add more then 100 classes, how does
this handle the line breaks?
You can join the whole array of black-listed strings and create a RegExp object with it as follows:
// I use a templated string and String.prototype.join
// to create a regular expression from a given array:
var classBlackListRegExp = new RegExp(`(${[
'col-sm-12',
'hidden-xs',
'hidden-sm',
'hidden-lg',
'hidden-md',
'active',
'^btn-.+'
].join("|")})`, "i");
var result = classBlackListRegExp.test("btn-whatever");
console.log(result);
You need to use Array#some and check against each value and return true if found.
function setupShouldAddClass(cssClass, filterClasses) {
return filterClasses.indexOf(cssClass) !== -1 || filterClasses.some(function (item) {
return stringContains(item, cssClass);
});
}
I would loop over them like this.
function setupShouldAddClass( cssClass, filterClasses )
{
// If the cssClass exists in the filterClasses then false
var ret = true;
filterClasses.forEach(function(el) {
if (cssClass.indexOf(el) >= 0) {
ret = false;
}
});
return ret;
}
How about bringing the cssClass to what you needed to be compared to:
var transformedToMyNeedsCSS = "btn-primary".replace(/^(btn[-])(?:.*)/,"$1");
// --> Output "bnt-"
And then you compare as you are doing it now:
if ( filterClasses.indexOf(transformedToMyNeedsCSS) > 0 )
{
return true;
}
There's a whole host going wrong here. Let's deal with it step by step:
First
if ( filterClasses.indexOf(cssClass) > 0 )
That is incorrect because indexOf returns -1 if the search term is not found. It returns 0 if it is the first item in the array. So you want >= 0.
Second, the forEach loop:
filterClasses.forEach(function ( item )
{
if ( stringContains(item, cssClass) )
{
return true;
}
});
This achieves nothing. As in, genuinely nothing. Because you are inside a new function (the callback to forEach) return only returns from the inner function. And the return value is then discarded. What this code actually does is loop all the way through the array and do nothing. What you actually want is a clever function called Array.prototype.some. This loops through the array and tests each element. If you return true on any of the elements, some returns true. Otherwise it returns false.
So your code could look like this:
return filterClasses.some(function(element) {
return stringContains(item, cssClass);
}
Now we want to ignore all classes where they begin with btn-. I presume this means that you want to return false if the class begins with btn-.
if (cssClass.indexOf('btn-') === 0) {
return false;
}
So your function now looks like:
function setupShouldAddClass( cssClass, filterClasses )
{
if (cssClass.indexOf('btn-') === 0) {
return false;
}
// If the cssClass exists in the filterClasses then false
if ( filterClasses.indexOf(cssClass) >= 0 )
{
return true;
}
return filterClasses.some(function(element) {
return stringContains(item, cssClass);
});
}
What it is supposed to do -
Example
url1(pages,"ALT") returns "www.xyz.ac.uk"
url1(pages,"xyz") returns ""
The error - TypeError: Cannot call method 'toUpperCase' of undefined
This is just for some coursework, Im stuck with these errors. Any help would be much appreciated
function index(string,pattern,caseSensitive) {
if(caseSensitive == false) {
var v = string.toUpperCase();
} else {
var v = string;
}
return indexNumber = v.indexOf(pattern);
}
var pages = [ "|www.lboro.ac.uk|Loughborough University offers degree programmes and world class research.", "!www.xyz.ac.uk!An alternative University" , "%www%Yet another University"];
alert(url1(pages, "ALT"));
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
if(pages[i].indexOf(seperator)>0){
siteContent = pages[i].split(pages[i].indexOf(seperator));
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
}
}
if(pages[i].indexOf(seperator)>0){
siteContent = pages[i].split(pages[i].indexOf(seperator));
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
If pages[i].indexOf(seperator)<=0, siteContent is still whatever it was from the last iteration. If that happens on the first iteration, siteContent is still [], and siteContent[2] is undefined.
Another problem: the expression pages[i].indexOf(seperator) returns a number, and pages[i].split expects a delimiting string as an argument. Since the number doesn't appear in your input, you'll always get a single-element array, and siteContent[2] will always be undefined. Get rid of .indexOf(seperator), change it to siteContent = pages[i].split(seperator).
One more: get rid of the else { return ""; }. Add a return ""; after the for loop.
Finally, in the first if statement condition, change .indexOf(seperator) > 0 to .indexOf(seperator, 1) !== -1. Since you're getting seperator from the first character of the string, it will be found at 0. You want the second occurrence, so start the search at 1. In addition, .indexOf returns -1 if it doesn't find the substring. You'll need to account for this in both if conditions.
Side note, as this is not causing your problem: never use == false. JS will coerce stuff like 0 and "" to == false. If that's what you want, just use the ! operator, because the expression has nothing to do with the value false.
My final answer is http://jsfiddle.net/QF237/
Right here:
alert(url1(pages, ALT)); // ALT ISN'T DEFINED
I believe you forgot to quote it:
alert(url1(pages, "ALT"));
You should split the string passing the separator character itself. Your function then will look like:
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
console.log(seperator);
if(pages[i].indexOf(seperator)>=0){
siteContent = pages[i].split(seperator); //fixed here
}
console.log(siteContent);
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
}
}
Tell us if it worked, please.
EDIT: It seeems your index() also has a little problem. Please try the function below.
function index(string,pattern,caseSensitive) {
var v;
if(caseSensitive == false) {
v = string.toUpperCase();
pattern = pattern.toUpperCase(); //to clarify: pattern should be uppercased also if caseSensitiveness is false
} else {
v = string;
}
return v.indexOf(pattern);
}
EDIT 2:
And url1() is finally like this:
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
if(pages[i].indexOf(seperator)>=0){
siteContent = pages[i].split(seperator);
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}
}
return "";
}
In this case, the first occurrence of pattern in all pages will be returned.
I'm having issues with conditionals. I want to return the index where pattern starts in string (or -1 if not found). The search is to be case sensitive if the 3rd parameter is true otherwise it is case insensitive.
Examples
index("abAB12","AB",true) returns 2 but index("abAB12","AB",false) returns 0
index("abAB12","BA",true) returns -1 and index("abAB12","BA",false) returns 1
Any idea how I can accomplish this?
This is my code so far
var s = "abAB12"
var p = "AB"
var cs = true
function index(string, pattern, caseSensitive) {
if (pattern) {
var found = false;
if (caseSensitive = false) {
if (string.indexOf(pattern.) >= 0) {
found = true;
}
return (found);
else {
return ("");
}
} else if (caseSensitive = true) {
if (string.toLowerCase().indexOf(pattern.toLowerCase()) >= 0) {
found = true;
}
return (found);
} else {
return ("");
}
}
}
alert(index(s, p, cs));
Fiddle at http://jsfiddle.net/AfDFb/1/
You have some mistype in your code. On the 15th line you have
}
return (found);
else {
This is not not valid. Change it to
return (found);
}
else {
There is another one.
if (caseSensitive = false) {
= used for assignment. You need to use == in if statements when comparing.
Also on the 13th line, there's an extra . after pattern. Remove it.
if (string.indexOf(pattern.) >= 0) {
Your fiddle example
You can use string.search() with a regular expression to accomplish this in one liner:
function index(input, key, caseMatters) {
return input.search(new RegExp(key, caseMatters ? '' : 'i'));
}
Now you can:
index("abAB12","AB",true); // returns 2
index("abAB12","AB",false); // returns 0
index("abAB12","BA",true); // returns -1
index("abAB12","BA",false); // returns 1
You need to use double equals sign == in your if, else statements.
if(caseSensitive == false)
And
if(caseSensitive == true)
You are assigning value inside if condition instead of comparing it.
Try
if (caseSensitive == false) {
and
if(caseSensitive == true)
You'd better use search :
'abAB12'.search(/AB/); // 2
'abAB12'.search(/AB/i); // 0
'abAB12'.search(/BA/); // -1
'abAB12'.search(/BA/i); // 1
The i flag means "case insensitive" ( insensible à la casse :D ).
Can I use 'in' to check existence of non top level names in a JSON data structure in a single comparison?
I have n tier JSON data structures,
I can do: if("mbled" in jsonData), works fine
For a lower tier name:
I can do this but (works but gets clunky as I go deeper): if("pnpenvsense1" in jsonData && "light" in jsonData.pnpenvsense1)
I'd prefer something like (doesn't work, always returns false): if("pnpenvsense1.light" in jsonData)
something like:
function objExists(path, struct){
path = path.split('.');
for(var i=0, l=path.length; i<l; i++){
if(!struct.hasOwnProperty(path[i])){ return false; }
struct = struct[path[i]];
}
return true;
}
objExists('pnpenvsense1.light', jsonData);
try
// will check if it is defined and not false
if(pnpenvsense1.light !== undefined && pnpenvsense1.light != false )
{ // my code }
http://jsfiddle.net/
arr = new Array();
arr['myKey'] = 12;
arr['myOtherKey'] = { "first" : "yes" , "second" : false };
if(arr.myKey !== undefined && arr.myKey != false)
alert("1 -> " + arr.myKey);
if(arr.myOtherKey.first !== undefined && arr.myOtherKey.first != false)
alert("2 -> " + arr.myOtherKey.first);