Function returning array item for assignment - javascript

I am implementing a drag'n'drop interface in js which has two tables of objects and a single object as source or destination. To execute the "move" operation, I thought of using these two class methods:
_getVar(id) {
var ind;
switch (id[0]) {
case 'c':
return this.clipboard;
case 's':
ind = Number(id.substr(1)) - 1;
return this.SynthPatches[ind];
case 'f':
ind = Number(id.substr(1)) - 1;
return this.FilePatches[ind];
}
}
move(from, to) {
var fv = this._getVar(from);
var tv = this._getVar(to);
if (fv == undefined) return "Source empty";
if (tv == undefined) return "Destination undefined";
tv = fv;
return "Ok";
}
This does not work. fv and tv are local references to the correct objects. The reference fv is copied to tv and then both are forgotten.
I would like something like a deep copy in python, but it looks like the only solution is to inline _getVar() nine times, which makes for really ugly code.

The easiest way to reduce duplication here is probably to add an optional second parameter to the function and allow it to set as well as get, e.g.:
_getOrSetVar(id, value) {
var ind;
switch (id[0]) {
case 'c':
if (value !== undefined) this.clipboard = value
return this.clipboard;
break;
case 's':
ind = Number(id.substr(1)) - 1;
if (value !== undefined) this.SynthPatches[ind] = value
else return this.SynthPatches[ind];
break;
case 'f':
ind = Number(id.substr(1)) - 1;
if (value !== undefined) this.FilePatches[ind] = value
else return this.FilePatches[ind];
break;
}
}
And perform your update like this:
this._getOrSetVar(to, this._getOrSetVar(from))

Related

Better way than using multiple if

I have a simple problem: I need to roll two sets of dice. For each number possible in the first roll, there are a set of conditionals that I then check against the second roll. The conditionals for each roll are different.
The problem, to me, is that something like this repeated roughly thirty times seems like a gross thing to stick in my code:
if (roll == 3) {
if (secondRoll < 5) {
do this
}
else if (secondRoll > 5 && secondRoll < 10) {
do this
}
else {
do this
}
}
if ...... and so on
So what do I do? Is there a more elegant way to do this? I've thought about something like this:
class Result {
constructor(firstRoll, secondRollMin, secondRollMax, output) {
this.firstRoll;
this.secondRollMin;
this.secondRollMax;
this.output;
}
}
and then checking the rolls against matching properties in a set of objects, but I'm not sure that's honestly any better. Any thoughts would be appreciated.
Just a small improvement, but you only need to check the upper bound of the range in each subsequence else if:
if (roll == 3) {
if (secondRoll < 5) {
do this
}
else if (secondRoll < 10) { // secondRoll must be >= 5 already
do this
}
else {
do this
}
}
Use Switch to simplify
switch (roll) {
case 1-4:
// Do something.
break;
case 5-8:
// Do something.
break;
case 9-11:
// Do something.
break;
default:
break;
}
How about creating a key for the combination and a bit simpler if/else? You could combine any combination that has the same action.
const combo = `${roll}-${secondRoll}`;
if (['1-1', '1-2', '1-3', '3-4', '3-5', '3-6'].includes(combo) {
// Do this
} else if (['1-4', '1-5', '1-6', '3-1', '3-2', '3-3'].includes(combo) {
// Do this
// ...
} else if (['6-4', '6-5', '6-6'].includes(combo) {
// Do this
}
Or create a switch/case:
const combo = `${roll}-${secondRoll}`;
switch (combo) {
case '1-1':
case '1-2':
case '1-3':
case '3-4':
case '4-5':
case '5-6':
// Do this
break;
case '1-4':
case '1-5':
case '1-6':
case '3-1':
case '4-2':
case '5-3':
// Do this
break;
// ...
case '6-4':
case '6-5':
case '6-6':
// Do this
break;
}
You can try a mixture of if, else and switch blocks. You can use different methods to call based on the first roll. A switch statement is usually more efficient than a set of nested ifs.
class Result {
constructor(firstRoll, secondRollMin, secondRollMax, output) {
this.firstRoll;
this.secondRollMin;
this.secondRollMax;
this.output;
switch(firstRoll){
case 1:
this.output = oneToXTime(secondRollMin,secondRollMin)
break;
case 2:
this.output = twoToXTime(secondRollMin,secondRollMax)
break;
case 3:
this.output = threeToXTime(secondRollMin,secondRollMax)
break;
case 4:
this.output = fourToXTime(secondRollMin,secondRollMax)
break;
case 5:
this.output = fiveToXTime(secondRollMin,secondRollMax)
break;
case 6:
this.output = sixToXTime(secondRollMin,secondRollMax)
break;
}
}
static String oneToXTime(secondRollMin,secondRollMin) {
String result = ""
if (secondRollMin < 5) {
result = "My result"
}
else if (secondRollMin < 10) {
result = "My result 2"
}
return result
}
static String twoToXTime(secondRollMin,secondRollMin) {
String result = ""
if (secondRollMin < 5) {
result = "My result"
}
else if (secondRollMin < 10) {
result = "My result 2"
}
return result
}
static String threeToXTime(secondRollMin,secondRollMin) {
// Same as above...
}
static Stfing fourToXTime(secondRollMin,secondRollMin) {
// Same as above...
}
static String fiveToXTime(secondRollMin,secondRollMin) {
// Same as above...
}
static String sixToXTime(secondRollMin,secondRollMin) {
// Same as above...
}
}

javascript : validate upper case and lower case

Is there any option to validate minimum 2 lower case and 2 upper case when checking case ?
here is the condition which I am using.
function HasMixedCase(passwd){
if(passwd.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))
return true;
else
return false;
}
Demo: http://jsfiddle.net/Ku4mg/
Edited to factor in plalx's comment:
var m;
if( !(m = passwd.match(/[a-z]/g)) || m.length < 2) return false;
if( !(m = passwd.match(/[A-Z]/g)) || m.length < 2) return false;
return true;
Trying to do too much in a single regex is a recipe for disaster, with the most common result being catastrophic backtracking.
Similarly, it makes your code more readable to do one thing at a time.
Altough #NietTheDarkAbsol's answer shows a perfectly valid way I would advise you to avoid doing too much in your functions. Do not be afraid of splitting logic into multiple maintainable functions.
function textCaseStats(text) {
return {
upper: (text.match(/[a-z]/g) || []).length,
lower: (text.match(/[A-Z]/g) || []).length
};
}
function hasMixedCase(password) {
var caseStats = textCaseStats(password);
return caseStats.lower >= 2 && caseStats.upper >= 2;
}

How do I convert the following to if statements?

So I play a RPG on a Virtual Tabletop that supports API. I do not want to create a bunch of objects individually, so I am working with the API, and want to read attributes from a JSON dump and then write those attributes to a object in the game (Character). So all goes well with my code so far as long as I am pulling static info. But in the case of Skills, they may or may not have all of the attr defined, so NULL. I would like to be able to identifiy the null and move on, not fail out because it is NULL.
I have attached a GIST, I am a n00b to this, and I am a System Engineer in real life, but not a coder. So I would appreciate the input!
https://gist.github.com/bigdadmike/7548421
Above is all of my code, but specifically this is the section I am working on, these have all been declared as var at top of script. I have updated this post with comments and suggestions so far from Basti. Code:
on('ready', function() {
_.each(monsterManual, function (monsterData){
log(monsterData.Name);
var character = createObj('character', {
name: monsterData.Name,
gmnotes: monsterData.FullText,
});
//*/
_.each(monsterAttributes, function(attr) {
var max = "";
var cur = "";
var re;
switch(attr){
case 'AC':
cur = parseInt(monsterData[attr].match(/(\d+)/)[1]);
break;
case 'Str':
case 'Dex':
case 'Con':
case 'Int':
case 'Wis':
case 'Cha':
re = new RegExp(attr + "\\s*(\\d*).*");
cur = parseInt(monsterData['AbilityScores'].match(re)[1]);
break;
case 'HD':
case 'Size':
case 'CR':
cur = monsterData[attr];
break;
case 'HP':
cur = parseInt(monsterData[attr]);
max = cur;
break;
case 'BaseAtk':
cur = parseInt(monsterData[attr]);
max = cur;
break;
case 'CMB':
cur = monsterData[attr];
max = cur;
break;
case 'CMD':
cur = parseInt(monsterData[attr]);
max = cur;
break;
case 'Acrobatics':
case 'Appraise':
case 'Bluff':
case 'Climb':
case 'Craft (any one)':
case 'Diplomacy':
case 'Disable Device':
case 'Disguise':
case 'Escape Artist':
case 'Fly':
case 'Handle Animals':
case 'Heal':
case 'Intimidate':
case 'Knowledge (religion)':
case 'Knowledge (planes)':
case 'Knowledge (history)':
case 'Knowledge (nature)':
case 'Knowledge (any one)':
case 'Linguistics':
case 'Perception':
case 'Ride':
case 'Sense Motive':
case 'Sleight of Hand':
case 'Spellcraft':
case 'Stealth':
case 'Survival':
case 'Swim':
case 'Use Magic Device':
re = RegExp(attr.replace('(', '\\(').replace(')', '\\)') + "\\s*(\\d*).*");
var match = re.exec(monsterData['Skills'])
if(match != null) {
cur = parseInt(monsterData['Skills'].match(re)[1]);
}
else {
cur = 0;
}
break;
default:
cur = parseInt(monsterData[attr]);
break;
}
log([attr, cur, max].join(':'))
if(cur != 0) {
createObj('attribute', {
characterid: character.id,
name: attr,
max: max,
current: cur
});
}
//*/
});
});
});
Basically
switch(attr) {
case 'AC':
//...
break;
case 'Str':
case 'Dex':
//...
break;
//...
}
will be
if(attr == 'AC') {
//...
} else if (attr == 'Str' || attr == 'Dex') {
//...
}
and so on. A case following another case directly will be converted to an or-expression.
BUT as the comments already point out, this is - with regard to readabilty - a bad idea...
You want to change this code section:
case 'Use Magic Device':
re = new RegExp(attr + "\\s*(\\d*).*");
cur = parseInt(monsterData['Skills'].match(re)[1]);
break;
to
case 'Use Magic Device':
re = RegExp(attr.replace('(', '\\(').replace(')', '\\)') + "\\s*(\\d*).*");
var match = re.exec(monsterData['Skills'])
if(match != null) {
cur = parseInt(match[1]);
} else {
cur = 0;
}
break;
This will check the match if it succeded (in this case the monster actually has the skill). If the match fails (the monst doesnt have this skill), then there won't be a parsing error and the break lets you hop out of the switch.
Further down you want to encase the call to createObject with an if:
if(cur != 0) {
createObj('attribute', {
characterid: character.id,
name: attr,
max: max,
current: cur
});
}
Furthermore, in your monsterAttributes there are two spelling errors which will cause cur to get NaN:
var monsterAttributes = [
/*...*/
'Disguise ', //there's a whitespace after Disguise, remove it!
/*...*/
'Use Magic device', //the 'd' has to be capital, as you check on 'Use Magic Device'
/*...*/
];
If I understood you correctly, you may simply check for attr being null and avoid the switch statement if this is the case. For example:
if( attr != undefined && attr != null ){
switch(attr){
//... the code as is in here
}// end switch
} // end if statement

Javascript: Ordinal suffix for numbers with specific CSS class

I am trying to display numbers within a particular table with ordinal suffixes. The table always shows three numbers which come from an XML file. The numbers show ranks, so for example they may be 6th, 120th, 131st. The output is a table that would look like this:
<table>
<tr>
<td class='ordinal'>6</td>
<td class='ordinal'>120</td>
<td class='ordinal'>131</td>
</tr>
</table>
I would ideally like to use javascript and I found a few very good solutions on stackoverflow, for example this one. However, I am struggling to apply the function to all numbers within the table, rather than putting in each number individually. I tried using a CSS class so that my function looks like this:
<script type="text/javascript">
$(function(){
$(".ordinal").each(function(){
var j = i % 10;
if (j == 1 && i != 11) {
return i + "st";
}
if (j == 2 && i != 12) {
return i + "nd";
}
if (j == 3 && i != 13) {
return i + "rd";
}
return i + "th";
});
})
</script>
but it's not working, probably because I screwed up the code somewhere. Maybe somebody here can help me out and tell me where I went wrong?
Thank you very much for your help!
My own suggestion, would be:
$(".ordinal").text(function (i, t) {
i++;
var str = i.toString().slice(-1),
ord = '';
switch (str) {
case '1':
ord = 'st';
break;
case '2':
ord = 'nd';
break;
case '3':
ord = 'rd';
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
ord = 'th';
break;
}
return i + ord;
});
JS Fiddle demo.
This effectively takes the incremented number (i++, in order to start from 1 not 0), converts it to a string, then looks at the last number of that string. This should work for any number, since the ordinal is based purely on that last number.
You could also extend the Number prototype to implement this functionality:
Number.prototype.ordinate = function(){
var num = this + 1,
last = num.toString().slice(-1),
ord = '';
switch (last) {
case '1':
ord = 'st';
break;
case '2':
ord = 'nd';
break;
case '3':
ord = 'rd';
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
ord = 'th';
break;
}
return num.toString() + ord;
};
$(".ordinal").text(function (i, t) {
return i.ordinate();
});
JS Fiddle demo.
Edited to offer a slight alternative:
Number.prototype.ordinate = function(){
var num = this,
last = num.toString().slice(-1),
ord = '';
switch (last) {
case '1':
ord = 'st';
break;
case '2':
ord = 'nd';
break;
case '3':
ord = 'rd';
break;
default:
ord = 'th';
break;
}
return num.toString() + ord;
};
$(".ordinal").text(function (i,t) {
return t.replace(/(\d+)/g, function(a){
return parseInt(a, 10).ordinate();
});
});
JS Fiddle demo.
This essentially iterates over each .ordinal element, replacing the numbers present with the (same) numbers with the ordinal suffix added to it.
Edited to address the problem, raised in the comments, below, that 11, 12 and 13 were receiving the ordinal suffix of st, nd and rd (respectively). This is now corrected to being th in all cases:
Number.prototype.ordinate = function(){
var num = this,
numStr = num.toString(),
last = numStr.slice(-1),
len = numStr.length,
ord = '';
switch (last) {
case '1':
ord = numStr.slice(-2) === '11' ? 'th' : 'st';
break;
case '2':
ord = numStr.slice(-2) === '12' ? 'th' : 'nd';
break;
case '3':
ord = numStr.slice(-2) === '13' ? 'th' : 'rd';
break;
default:
ord = 'th';
break;
}
return num.toString() + ord;
};
JS Fiddle demo.
References:
slice().
switch().
text().
toString().
function nth(n){
if(isNaN(n) || n%1) return n;
var s= n%100;
if(s>3 && s<21) return n+'th';
switch(s%10){
case 1: return n+'st';
case 2: return n+'nd';
case 3: return n+'rd';
default: return n+'th';
}
}
You can take care of the teens in their own line, other integers follow the last digit rules.
I created two approaches one using Prototype, the other as a plugin :
Number.prototype.between = function(n,m){ return this > n && this < m }
Number.prototype.ORDINATE_INDEX = ["th","st","nd","rd"];
Number.prototype.toOrdinate = function(){
var
nthMod = (this % 10),
index = nthMod > 3 || this.between(10,20) ? 0 : nthMod
;
return this + this.ORDINATE_INDEX[index];
};
$(".ordinal").text(function (index, element) {
return parseInt(element).toOrdinate();
});
This is the one as a Jquery Plugin :
(function($){
var numberTool = new (function(){
var private = {};
private.ORDINATE_INDEX = ["th","st","nd","rd"];
private.parseOrdinary = function(number)
{
var
nthMod = (number % 10),
index = nthMod > 3 || private.between(number, 10,20) ? 0 : nthMod
;
return number + private.ORDINATE_INDEX[index];
}
private.between = function(number, n,m){
return number > n && number < m
}
this.isNumeric = function(number)
{
return !isNaN(parseFloat(number)) && isFinite(number);
}
this.toOrdinary = function(number)
{
return this.isNumeric(number) ? private.parseOrdinary(number) : number;
}
});
$.fn.toOrdinary = function(){
return this.each(function(){
$element = $(this);
$element.text(numberTool.toOrdinary($element.text()));
});
};
})(jQuery);
$(".ordinal").toOrdinary();
$(".ordinal").toOrdinary();
$(".ordinal").toOrdinary();
Tested on JSFIDDLE:
The prototype version example : http://jsfiddle.net/f8vQr/6/
The JQuery version example : http://jsfiddle.net/wCdKX/27/
It's not working because you're returning the strings to $.each, not actually using them. Usage would depend on your HTML but here is an example of setting the .ordinal text to the value.
You were also missing the i parameter on the event handler and you can increment i to start from 1st instead of 0th.
jsFiddle
$(".ordinal").each(function (i) {
i++;
var j = i % 10,
str;
if (j == 1 && i != 11) {
str = i + "st";
} else if (j == 2 && i != 12) {
str = i + "nd";
} else if (j == 3 && i != 13) {
str = i + "rd";
} else {
str = i + "th";
}
$(this).text(str);
});
If you have the numbers in your elements already then it would be best to not rely on the index and instead check the number, then append the string to the end.
jsFiddle
$(document).ready(function () {
$(".ordinal").each(function (i) {
var j = parseInt($('ordinal').text(), 10) % 10,
str;
if (j == 1 && i != 11) {
str = "st";
} else if (j == 2 && i != 12) {
str = "nd";
} else if (j == 3 && i != 13) {
str = "rd";
} else {
str = "th";
}
var elem = $(this)
elem.text(elem.text() + str);
});
});
Ordinal suffix in one line
var integerWithSuffix=originalInteger+(['st','nd','rd'][( originalInteger +'').match(/1?\d\b/)-1]||'th');
the concatenation of the original number and a string representing the ordinal derived from an array indexed by the result of a regex search on that number
http://jsfiddle.net/thisishardcoded/DbSMB/
I would do something like this, based on David Thomas's answer:
Number.prototype.ordinate = function(){
var num = this,
ones = num % 10, //gets the last digit
tens = num % 100, //gets the last two digits
ord = ["st","nd","rd"][ tens > 10 && tens < 20 ? null : ones-1 ] || 'th';
return num.toString() + ord;
};
It accomplishes the same thing. If a number's last 2 digits are within the 11-19 range OR the last digit is between 4-0 it defaults to 'th', otherwise it will pull a 'st', 'nd' or 'rd' out of the array based on the ones place.
I like the idea of creating a prototype function very much but I would definitely leave the incrementation of the index outside of the prototype function to make it more versatile:
$(".ordinal").text(function (i, t) {
return (++i).ordinate();
});
JS Fiddle Demo
function ordsfx(a){return["th","st","nd","rd"][(a=~~(a<0?-a:a)%100)>10&&a<14||(a%=10)>3?0:a]}
See annotated version at https://gist.github.com/furf/986113#file-annotated-js
Short, sweet, and efficient, just like utility functions should be. Works with any signed/unsigned integer/float. (Even though I can't imagine a need to ordinalize floats)
This is what I use, and it works for any year, month, day (leap year) included:
// panelyear, panelmonth and panelday are passed as parameters
var PY01 = parseInt(panelyear); var PM01 = (parseInt(panelmonth) - 1); PD01 = parseInt(panelday);
var now = new Date(PY01, PM01, PD01);
var start = new Date(PY01, 0, 0);
var diff = (now - start) + ((start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000);
var oneDay = 1000 * 60 * 60 * 24;
var day = Math.floor(diff / oneDay);
function getNumberWithOrdinal(n) { var s = ["th","st","nd","rd"], v = n % 100; return n + (s[(v - 20) % 10] || s[v] || s[0]); }
Use with
<script> document.write(getNumberWithOrdinal(day)); </script>

Does switch statement start comparing cases from the top in this example?

I found this example to make range work with switch statement:
function GetText(value)
{
var result;
switch (true)
{
case ((value >= 26) && (value <= 50)):
result = ">= 26.";
break;
case ((value >= 1) && (value <= 25)):
result = "Between 1 and 25.";
break;
case (value == 0):
result = "Equals Zero.";
break;
}
return result;
}
But if I modify the code and remove the second check for the value the example will still work:
function GetText(value)
{
var result;
switch (true)
{
case ((value >= 26)):
result = ">= 26 .";
break;
case ((value >= 1)):
result = "Between 1 and 25.";
break;
case (value == 0):
result = "Equals Zero.";
break;
}
return result;
}
So if I passed 29 even that I have two true cases the first one will be selected. My question is that how switch statement works in most of programming languages it will start comparing from the top or its only in this case (and is it good or bad to write it like that?).
switch statement checks for matches from top to bottom.
From MDN docs on switch statement:
If a match is found, the program executes the associated statements. If multiple cases match the provided value, the first case that matches is selected, even if the cases are not equal to each other.
I would do something like this (with if and else if chains):
function GetText(value) {
var result;
if (value == 0) {
result = "Equals Zero.";
} else if (value <= 25) {
result = "Between 1 and 25.";
} else if (value <= 50) {
result = "Between 26 and 50.";
}
return result;
}

Categories