Translating two javascript terms into AS3 - javascript

I'm working with a JS script that someone developed for me but I need to convert it to AS3. I'm good with AS3 but a bit baffled re: how to translate these two lines into AS3.
var teams = Array.apply(null, {length: numOfTeams}).map(Number.call, Number)
the following snippet with backwards single quotes
pairs[`${i},${x}`] = true;
Although the script runs fine in a JS interpreter, I'm not sure what "`" denotes and I'm thinking that "apply" and "map" may have been around in AS2 when it was prototype-based but I never used AS2. Suggestions?

So in modern JS the back tick represents string interpolation. I don't think there is an equivalent in AS3. Inside back ticks you can use ${} to wrap a variable in a string.
Taking the example and imagining some input
var i = 1;
var x = 2;
`${i},${x}`
// outputs "1,2" as a string
var i = "Foo";
var x = "Bar";
`${i},${x}`
// outputs "Foo,Bar" as a string
It just concatenates two variables with a comma in between.
It could be written the same way.
var i = 1;
var x = 2;
i + "," + x
// outputs "1,2" as a string
It reads like you have an Object pairs with i comma x as a key and boolean as a value.
{
"1,2": true,
"0,0": false,
"4,5": true,
"9,3": true
}

`` denotes a template literal, which means that ${i},${x} will be replaced with whatever the variable i contains, a comma, and then whatever the variable x contains. The following two are equivalent:
pairs = {};
i = 'hello'
x = 'world'
pairs[`${i},${x}`] = true;
console.log(pairs)
pairs = {}
pairs[i + ',' + x] = true
console.log(pairs)
.apply looks like it existed in AS3, as does .map

Related

If strings are immutable in Javascript, why does the += operator work the way it does?

Everything I'm reading about modifying strings in Javascript says that strings are immutable. Hence why concat returns a new string that is a modification of the original:
let originalString = 'hello';
let modifiedString = originalString.concat(' world');
console.log('originalString:', originalString);
console.log('modifiedString:', modifiedString);
results in:
"originalString: hello"
"modifiedString: hello world"
So far it makes sense. The original string remains even after concat because strings are immutable.
However, if I do the following:
let originalString2 = 'hello';
let modifiedString2 = originalString2 += ' world';
console.log('originalString2:', originalString2);
console.log('modifiedString2:', modifiedString2)
the result is:
"originalString2: hello world"
"modifiedString2: hello world"
This sure seems like it's mutating the original string to me. If that is not what's happening, can someone explain it to me? I've even found articles that go over the ways to modify strings that say at the beginning "Javascript strings are immutable" and later go on to say that the += method is mutating the original string, so I'm getting contradictory information.
JSFiddle of the simple test
I think it's because of the short hand addition operator (+=) you're using.
It's actually doing 2 operations first addition then assignment.
let x = 2;
x += 2;
// The above line is equivalent to the following.
x = x + 2;
So for you're example
let str = "hello";
str += "world"; // is the same as
str = str + "world";
So we are making a new string by str + "world" then assigning it to our str variable. So it's still immutable :)

Evaluating escaped string literals in Javascript

Consider the following code snippet:
var a = '\\555';
var b = '\555';
console.log(a, b); // outputs \555 -5
So the value of a has an escaped back-slash and b has escaped 55 which is the octal equivalent of the Unicode code-point of the "minus" or "dash" and the trailing 5 is concatenated to the - resulting in -5.
Now I want to the best way to "evaluate/decode" the value of a in such a way that it is equal to the value of b. Any help would be appreciated.
eval can do this (assuming it's a valid string and contains no other escapes and no quotes):
var a = '\\555';
console.log(eval('"' + a '"'));
but indeed an explicit decode function is a better idea:
function decodeOctEscapes(x) {
return x.replace(/\\([0-7]{2})/g, (_, v) => String.fromCharCode(parseInt(v, 8)));
}
There are other ways except Regex, but none of them is ideal:
var a1 = '\\555';
// unsafe: only for trusted values
var a2 = eval('"' + a1 + '"');
var b1 = '\555';
// ES6 raw string; cannot be created on runtime
var b2 = String.raw`\555`;
console.log(a1===b2) // true
console.log(a2===b1) // true

Parse a string representing an array of floats

I have a string as such:
string = "[x,y,z]"
Where x, y and z are valid javascript floats as strings. Some examples:
-0.9999
1.
1.00000000000E-5
-1E5
What is the most efficient (fastest) way to parse this string into an actual javascript array of floats without using Eval?
Now I do this:
parseFloatArray = function(string){
// isolate string by removing square brackets
string = string.substr( 1, string.length-2 )
// create array with string split
var array = string.split(',');
// parse each element in array to a float
for (var i = 0, il = array.length; i < il; i++){
array[i] = parseFloat(array[i]);
}
// return the result
return array
}
It is important that the solution works correctly for the above examples.
I also tried with JSON.parse which seemed perfect at first, but it returns a SyntaxError for the second example 1. where there is nothing following the decimal separator.
I prepared a fiddle for testing.
Instead of this
array[i] = parseFloat(array[i]);
try
array[i] = +array[i];
Above handles all the test cases pretty well.
Here is the working fiddle
Try this :
str = "[-0.9999, 1., 1.00000000000E-5,-1E5]";
str.slice(1, str.length-1).split(',').map(Number);
// [-0.9999, 1, 0.00001, -100000]
parseFloat basic syntax is parseFloat(string). https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
If you think the input values are only numbers you can use Number(x) rather than parseFloat.
Also, you might get different values upon parsing because all floating-point math is based on the IEEE [754] standard. so use toPrecision() method to customize your values- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Number/toPrecision.
Instead of you writing the for loop, you can always use the javascript built in array.prototypes
var string = "[1.,-0.9999,-1.00000E-5]";
var array = string.substr( 1, string.length-2 ).split(',');
console.log(array.map(function(i){return parseFloat(i);}));
you can also use the unary operator instead of parseFloat().
console.log(array.map(function(i){return +i;}));
Most efficient to extract is replacing and splitting.
var str = "[312.413,436.6546,34.1]"; // string
str = () => str.replace(/\[|\]/g, "").split(","); //[312.413, 4...] Array
most eficient to parse is just preceed the string with a "+", that will numerify it and, since javascript works with floats as primitives (and not integers), they will be automatically parsed.
var a = "1.00000000000E-5";
console.log( +a ); // 0.00001

Prevent tainting properties of the RegExp constructor in JavaScript

This is a bit of a conundrum, I have an idea of how I might be able to fix it but I'm wondering if there's a (much) easier way.
In short, whenever a regular expression is executed in JavaScript, certain properties are assigned values on the RegExp constructor. For instance:
/foo/.test('football')
//-> true
RegExp.input
//-> "football"
RegExp.rightContext
//-> "tball"
I'd like to execute a regular expression without affecting these properties. If that's not possible (and I don't think it is), I'd like to at least restore them to their previous values afterwards.
I know input/$_ is writeable, but most of the others aren't, it seems. One option might be to reconstruct a regular expression that would reapply all these values, but I think that would be quite difficult.
The reason I want this is because I'm writing a shim of a native API, and testing it using the test262 suite. The test262 suite fails on certain tests where it checks to see if the RegExp object has unexpected values for these properties.
You can try to create a wrapper function for test:
var fTest = RegExp.test;
RegExp.test = function() {
var bReturn = fTest.apply(RegExp, arguments);
delete RegExp.input;
delete RegExp.rightContext;
return bReturn;
}
This is the final result. It's a little more robust than my initial effort; it properly escapes sub-expressions, makes sure they appear in the right order and doesn't stop when it finds an empty one:
/**
* Constructs a regular expression to restore tainted RegExp properties
*/
function createRegExpRestore () {
var lm = RegExp.lastMatch,
ret = {
input: RegExp.input
},
esc = /[.?*+^$[\]\\(){}|-]/g,
reg = [],
cap = {};
// Create a snapshot of all the 'captured' properties
for (var i = 1; i <= 9; i++)
cap['$'+i] = RegExp['$'+i];
// Escape any special characters in the lastMatch string
lm = lm.replace(esc, '\\$0');
// Now, iterate over the captured snapshot
for (var i = 1; i <= 9; i++) {
var m = cap['$'+i];
// If it's empty, add an empty capturing group
if (!m)
lm = '()' + lm;
// Else find the escaped string in lm wrap it to capture it
else
lm = lm.replace(m.replace(esc, '\\$0'), '($0)');
// Push to `reg` and chop `lm`
reg.push(lm.slice(0, lm.indexOf('(') + 1));
lm = lm.slice(lm.indexOf('(') + 1);
}
// Create the property-reconstructing regular expression
ret.exp = RegExp(reg.join('') + lm, RegExp.multiline ? 'm' : '');
return ret;
}
It does what I originally thought to be difficult. This should restore all the properties to their former values, if you use it like so:
var
// Create a 'restore point' for RegExp
old = createRegExpRestore(),
// Run your own regular expression
test = someOtherRegEx.test(someValue);
// Restore the previous values by running the RegExp
old.exp.test(old.input);

Template and Place holders algorithm

First a quick definition :)
Template - A string which may contain placeholders (example:"hello [name]")
Placeholder - A substring whitin square brackets (example: "name" in "hello [name]:).
Properties map - A valid object with strings as values
I need to write a code that replace placeholders (along with brackets) with the matching values in the properties map.
example:
for the following properties map:
{
"name":"world",
"my":"beautiful",
"a":"[b]",
"b":"c",
"c":"my"
}
Expected results:
"hello name" -> "hello name"
"hello [name]" -> "hello world"
"[b]" -> "c"
"[a]" -> "c" (because [a]->[b]->[c])
"[[b]]" -> "my" (because [[b]]->[c]->my)
"hello [my] [name]" -> "hello beautiful world"
var map = {
"name":"world",
"my":"beautiful",
"a":"[b]",
"b":"c",
"c":"my"
};
var str = "hello [my] [name] [[b]]";
do {
var strBeforeReplace = str;
for (var k in map) {
if (!map.hasOwnProperty(k)) continue;
var needle = "[" + k + "]";
str = str.replace(needle, map[k]);
}
var strChanged = str !== strBeforeReplace;
} while (strChanged);
document.write(str); //hello beautiful world my
The answer by #chris is excellent, I just want to provide an alternative solution using regular expressions that works "the other way round", i.e., not by looking for occurrences of the "placeholder versions" of all items in the properties map, but by repeatedly looking for occurrences of the placeholder itself, and substituting it with the corresponding value from the property map. This has two advantages:
If the property map grows very large, this solution should have
better performance (still to be benchmarked though).
The placeholder and the way substitutions work can easily be modified by adjusting the regular expression and the substitution function (might not be an issue here).
The downside is, of course, that the code is a little more complex (partly due to the fact that JavaScript lacks a nice way of substituting regular expression matches using custom functions, so that's what substituteRegExp is for):
function substituteRegExp(string, regexp, f) {
// substitute all matches of regexp in string with the value
// returned by f given a match and the corresponding group values
var found;
var lastIndex = 0;
var result = "";
while (found = regexp.exec(string)) {
var subst = f.apply(this, found);
result += string.slice(lastIndex, found.index) + subst;
lastIndex = found.index + found[0].length;
}
result += string.slice(lastIndex);
return result;
}
function templateReplace(string, values) {
// repeatedly substitute [key] placeholders in string by values[key]
var placeholder = /\[([a-zA-Z0-9]+)\]/g;
while (true) {
var newString = substituteRegExp(string, placeholder, function(match, key) {
return values[key];
});
if (newString == string)
break;
string = newString;
}
return string;
}
alert(templateReplace("hello [[b]] [my] [name]", {
"name":"world",
"my":"beautiful",
"a":"[b]",
"b":"c",
"c":"my"
})); // -> "hello my beautiful world"
Update: I did some little profiling to compare the two solutions (jsFiddle at http://jsfiddle.net/n8Fyv/1/, I also used Firebug). While #chris' solution is faster for small strings (no need for parsing the regular expression etc), this solution performs a lot better for large strings (in the order of thousands of characters). I did not compare for different sizes of the property map, but expect even bigger differences there.
In theory, this solution has runtime O(k n) where k is the depth of nesting of placeholders and n is the length of the string (assuming dictionary/hash lookups need constant time), while #chris' solution is O(k n m) where m is the number of items in the property map. All of this is only relevant for large inputs, of course.
If you're familiar with .NET's String.Format, then you should take a look at this JavaScript implementation. It supports number formatting too, just like String.Format.
Here's an example of how to use it:
var result = String.Format("Hello {my} {name}", map);
However, it would require some modification to do recursive templates.

Categories