The enumeration of JS objects seems to be inconsistent in Firefox.
Code:
var a = {"3":"a", "2":"b", "foo":"c", "1":"d"};
var str = "";
for(var n in a) { str += n + " = " + a[n] + "\n"; }
alert(str);
Result with FF22 on Windows:
1 = d
2 = b
3 = a
foo = c
Result expected (and what I get with FF20 on Linux):
3 = a
2 = b
foo = c
1 = d
How can I keep the Elements in the same order as inserted?
I know the ECMA specification doesn't say how the enumeration should be done, therefore it can't be called a bug. But I need the elements in the order inserted. (Reason: I get a JSON-encoded hash-table which is ordered server-side. Until recently the order was kept, now the whole list is a mess because it's ordered by the keys)
No, you can't get that. The order of keys in object is undetermined and can be anything JS implementation wants.
You can, however, sort the keys:
var a = {"3":"a", "2":"b", "foo":"c", "1":"d"};
var str = "";
for(var n in Object.keys(a).sort(your_sorting_function)) {
str += n + " = " + a[n] + "\n";
}
alert(str);
your_sorting_function should accept two arguments, which effectively will be names of the keys in a object. Then this function should return 0 for identical keys, 1 if the first key is considered "bigger" and -1 otherwise.
Object.keys() does not exist in all the browsers, but there's plenty of implementations that can be found.
For example:
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
Related
Recently Attended the interview, Some one asked the question like below:
var array = [0,1,2,3,4,5];
Output :
temp:
temp1:1
temp22:22
temp333:333
temp4444:4444
temp55555:55555
I tried below code it is working fine but is there any best solution for this example :
array.forEach(function(item,index){
var text ="";
if(index >= 2){
for(var j =1; j <= index; j++){
text += index;
}
console.log("temp"+text + ":" + text);
}else{
console.log("temp"+index + ":" + index);
}
});
Thanks in advance!
Using ES6 template strings and String.prototype.repeat
var array = [0,1,2,3,4,5];
array.forEach(item => {
const text = String(item).repeat(item);
console.log(`temp${text}: ${text}`);
})
And the same code translated into ES5 - this will work in all browsers starting from IE9 and above.
var array = [0,1,2,3,4,5];
array.forEach(function(item) {
var text = Array(item+1).join(item);
console.log("temp" + text + ": " + text);
})
Since String.prototype.repeat does not exist in ES5, there is a bit of a hack to generate a string of specific length with repeating characters:
Array(initialCapacity) will create a new array with empty slots equal to what number you pass in, Array.prototype.join can then be used to concatenate all members of the array into a string. The parameter .join takes is the separator you want, so, for example you can do something like this
var joinedArray = ["a","b","c"].join(" | ");
console.log(joinedArray);
However, in this case, each of the members of the array is blank, since the array only has blank slots. So, upon joining, you will get a blank string, unless you specify a separator. You can leverage that to get a repeat functionality, as you are essentially doing something like this
//these produce the same result
var repeatedA = ["","",""].join("a");
var repeatedB = Array(3).join("b");
console.log("'a' repeated:", repeatedA);
console.log("'b' repeated:", repeatedB);
Using the Array function, you can scale it to any number of repeats you want. The only trick is that you need to add 1 when creating the array, since you get one less character when joining.
You could iterate the array and iterate the count. Then display the new string.
var array = [0, 1, 2, 3, 4, 5];
array.forEach(function (a, i) {
var s = '';
while (i--) {
s += a;
}
console.log ('temp' + s + ':' + s);
});
I need an hash table in Javascript, i.e. to implement an associative array which maps keys (strings) to values (in my case, these are several integer arrays). I realized that this kind of approach is not commonly used, or at least I haven't found it on the web yet:
var hash = ['s0'];
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
hash['s'+i] = [r, r*2, r^2];
}
console.log(hash);
hash.forEach(function (v, i, a) {
document.getElementById('foreach').innerHTML += i + ' => ' + v + '<br>';
})
for (var i = 5; i >= 1; i--) {
var key = 's'+i;
document.getElementById('for').innerHTML += key + ' => [' + hash[key].toString() + ']<br>';
}
<p id="foreach">forEach (val,index):<br/></p>
<p id="for">for:<br/></p>
Perhaps due to the fact that the declared array seems to not be correctly mapped after I add the new values (open your console and click the + button, you can see the values are there even when it displays [s1]). The forEach keeps assuming the array only has one value, but if I access any of those keys directly, e.g. hash['s3'], the respective array is returned.
Therefore, am I doing something wrong? Should I even use this approach?
If objects in JSON are more appropriate for this case, what is the best way to implement something simple and similar to the example above?
Furthermore, if key_string is the string I want as key, hash.push(key_string: val_array) fails because it is not a "formal parameter". However by doing something like:
hash.push({'key':key_string,'value':val_array})
How can I access one of those arrays in the simplest way possible through its associated key?
Why can't you use a JavaScript Map()?
MDN JavaScript Reference: Map
I modified your code below to use a Map instead of an Array:
var map = new Map();
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
map.set('s'+i, [r, r*2, r^2]);
}
console.log(map);
map.forEach(function (v, i, m) {
document.getElementById('foreach').innerHTML += i + ' => ' + v + '<br>';
})
for (var i = 5; i >= 1; i--) {
var key = 's'+i;
document.getElementById('for').innerHTML += key + ' => [' + map.get(key).toString() + ']<br>';
}
<p id="foreach">forEach (val,index):<br/></p>
<p id="for">for:<br/></p>
Javascript's object type covers all the behavior you are looking for:
var obj = {};
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
obj['s'+i] = [r, r*2, r^2];
}
The cool thing about the object type in Javascript is you can access properties using the associate array-like syntax or using dot notation.
obj['key'] === obj.key
Please, check this example in the console.
var hash = {'s0':' '};
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
hash['s'+i] = [r, r*2, r^2];
}
see that hash object now contains key-value mapping
console.log(hash);
to access the object with forEach you can extract the keys of the object as an array and iterate over it:
Object.keys(hash).forEach(function (v, i, a) {
console.log( i , v, hash[v] );
})
You may also start using such libraries as https://lodash.com/ that implement a number of common operations over collections.
I know that console.log supports at least some of the basic features of printf from C through messing around, but I was curious of a way to take advantage of console.log's implementation to create something similar to sprintf. I know you can't simply use .bind or .apply since console.log doesn't actually return the string, so is there a way around this?
If this isn't actually possible, is there some other little-known native implementation that's only a few lines of code away from achieving sprintf in JavaScript?
For those who do not know what sprintf is exactly, here is some documentation from tutorialspoint. Example usage I'm looking for is below:
var string1 = sprintf("Hello, %s!", "world");
var string2 = sprintf("The answer to everything is %d.", 42);
Keep it simple
var sprintf = (str, ...argv) => !argv.length ? str :
sprintf(str = str.replace(sprintf.token||"$", argv.shift()), ...argv);
Since Javascript handles data types automatically, there is no need for type options.
If you need padding, "15".padStart(5,"0") = ("00000"+15).slice(-5) = "00015".
Usage
var sprintf = (str, ...argv) => !argv.length ? str :
sprintf(str = str.replace(sprintf.token||"$", argv.shift()), ...argv);
alert(sprintf("Success after $ clicks ($ seconds).", 15, 4.569));
sprintf.token = "_";
alert(sprintf("Failure after _ clicks (_ seconds).", 5, 1.569));
sprintf.token = "%";
var a = "%<br>%<br>%";
var b = sprintf("% plus % is %", 0, 1, 0 + 1);
var c = sprintf("Hello, %!", "world");
var d = sprintf("The answer to everything is %.", 42);
document.write(sprintf(a,b,c,d));
Try utilizing eval , .replace
var sprintf = function sprintf() {
// arguments
var args = Array.prototype.slice.call(arguments)
// parameters for string
, n = args.slice(1, -1)
// string
, text = args[0]
// check for `Number`
, _res = isNaN(parseInt(args[args.length - 1]))
? args[args.length - 1]
// alternatively, if string passed
// as last argument to `sprintf`,
// `eval(args[args.length - 1])`
: Number(args[args.length - 1])
// array of replacement values
, arr = n.concat(_res)
// `res`: `text`
, res = text;
// loop `arr` items
for (var i = 0; i < arr.length; i++) {
// replace formatted characters within `res` with `arr` at index `i`
res = res.replace(/%d|%s/, arr[i])
}
// return string `res`
return res
};
document.write(sprintf("%d plus %d is %d", 0, 1, 0 + 1)
+ "<br>"
+ sprintf("Hello, %s!", "world")
+ "<br>"
+ sprintf("The answer to everything is %d.", 42)
);
i m trying to calculate weight of a string using the following function
function weight(w)
{
Cap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
small = 'abcdefghijklmnopqrstuvwxyz'
spcl = "~!##$%^&*()_+[]\{}|;':,./<>?"
num = '0123456789'
var p = []
for(i=0;i<w.length;i++)
{
if(Cap.contains(w[i])==true)
p[i] = Cap.indexOf(w[i]) + 2
else if(small.contains(w[i])==true)
p[i] = small.indexOf(w[i]) + 1
else if(num.contains(w[i]))
p[i] = num.indexOf(w[i])
else if(spcl.contains(w[i]))
p[i] = 1
}
return _.reduce(p,function(memo, num){ return memo + num; }, 0);
}
where w is a string. this properly calculates weight of the string.
But whn i try to to calculate weight of strings given in a an array, it jst calculates the weight of the first element, ie. it does not run the full for loop. can anyone explain to me why is that so??
the for loop is as given below
function weightList(l)
{
weigh = []
for(i=0;i<l.length;i++)
weigh.push(weight(l[i]));
return weigh;
}
input and output:
>>> q = ['abad','rewfd']
["abad", "rewfd"]
>>> weightList(q)
[8]
whereas the output array should have had 2 entries.
[8,56]
i do not want to use Jquery. i want to use Vanilla only.
Because i is a global variable. So when it goes into the function weight it sets the value of i greater than the lenght of l. Use var, it is not optional.
for(var i=0;i<l.length;i++)
and
for(var i=0;i<w.length;i++)
You should be using var with the other variables in the function and you should be using semicolons.
I think your issue is just malformed JavaScript. Keep in mind that JavaScript sucks, and is not as forgiving as some other languages are.
Just by adding a few "var" and semicolons, I was able to get it to work with what you had.
http://jsfiddle.net/3D5Br/
function weight(w) {
var Cap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
small = 'abcdefghijklmnopqrstuvwxyz',
spcl = "~!##$%^&*()_+[]\{}|;':,./<>?",
num = '0123456789',
p = [];
for(var i=0;i<w.length;i++){
if(Cap.contains(w[i])==true)
p[i] = Cap.indexOf(w[i]) + 2
else if(small.contains(w[i])==true)
p[i] = small.indexOf(w[i]) + 1
else if(num.contains(w[i]))
p[i] = num.indexOf(w[i])
else if(spcl.contains(w[i]))
p[i] = 1
}
return _.reduce(p,function(memo, num){ return memo + num; }, 0);
}
function weightList(l) {
var weigh = [];
for(var i=0;i<l.length;i++)
weigh.push(weight(l[i]));
return weigh;
}
q = ['abad','rewfd'];
results = weightList(q);
Hope that helps
Here is my code so far for my school project (using Murach's JavaScript and DOM Scripting by Ray Harris). The chapter is only about Arrays and does not cover Prototypes, but I wanted to try it out based on Internet tutorials and references:
/*
Operation
This application stores the last name, first name, and score for
one or more students and it calculates the average score for all of the scores
that have been entered. When the user clicks on the Clear button, this
application clears the score data from this application. When the user clicks
on the Sort button, this application sorts the data in alphabetical order by
last name.
Specifications
The program should use one or more arrays to store the data.
Assume that the user will enter valid data.
*/
var $ = function (id)
{
return document.getElementById(id);
}
/*
Array prototype object extension for averaging the contents
"Adding a method to the built-in Array object to extract the average
of any numerical values stored in the array is therefore a useful
addition to that object." http://javascript.about.com/library/blaravg.htm
*/
Array.prototype.average = function ()
{
var avg = 0;
var count = 0;
for (var i = 0; i<this.length; i++)
{
//never gets here:
alert(i + ": " + this[i]);
var e = +this[i];
if(!e && this[i] !== 0 && this[i] !== '0')
{
e--;
}
if (this[i] == e)
{
avg += e;
count++;
}
}
return avg / count;
}
var addScore = function ()
{
studentScores[$('last_name').value + ', ' + $('first_name').value] = $('score').value;
update();
}
var clearScore = function ()
{
for (var i in studentScores)
{
studentScores[i] = '';
}
update();
}
var sortScore = function ()
{
scores.sort();
update();
}
var update = function ()
{
var result = '';
for (var i in studentScores)
{
result += (i + ': ' + studentScores[i] + '\n');
}
$('scores').value = result;
$('average_score').value = studentScores.average().toFixed(1);
}
window.onload = function ()
{
//a variable is initialized inside a function without var, it will have a global scope:
studentScores = [];
$('add_button').onclick = addScore;
$('sort_button').onclick = sortScore;
$('clear_button').onclick = clearScore;
$('last_name').focus();
}
When the code enters the "update()" function (end of the "addScore()" function) and accesses the array,
it populates the "literal" code from the Prototype into the text area (and fails to find the average on the next line):
I don't have enough rep points to post the image, but here is my output (there are no errors in the Chrome JS Console):
lowe, doug: 82
average: function ()
{
var avg = 0;
var count = 0;
for (var i = 0; i<this.length; i++)
{
//never gets here:
alert(i + ": " + this[i]);
var e = +this[i];
if(!e && this[i] !== 0 && this[i] !== '0')
{
e--;
}
if (this[i] == e)
{
avg += e;
count++;
}
}
return avg / count;
}
Any help appreciated (best practice or algorithm suggestions welcome)
Change this:
studentScores = []
to this:
studentScores = {}
...so that you're using an Object instead of an Array.
Your for loop in average() is just iterating numeric indices instead of the non-numeric keys you created.
Create your average() method as a standalone function like the others, and pass studentScores to it to calculate the average, and then use for-in instead of for.
That's simple: Do not use for…in enumerations for looping Arrays! You do so in your clearScore and update functions.
for (var prop in obj) loops over all [enumerable] properties, including those that are inherited from Array.prototype (for Array objects at least). A for (var i=0; i<array.length; i++) loop will not have that problem.
You have to decide whether studentScores is intended to be an array (i.e., an integer is used to access the stored data) or an Object/Associative Array (a string is used to set/get an element).
If you want to use the student's name as the key, you should declare studentScores as an object, and your 'average' method would have to be added to the Object prototype (which I don't recommend).
With the current state of the code, you have stumbled on the fact that an Array is also an object, and can have arbitrary properties attached to it, like any other object. You have added properties by name, but in your average method, you are trying to access numerically based indices. But that's not where the data you're adding is stored.
> a = [];
[]
> a['foo'] = 'bar';
'bar'
> a.length
0
> a[3] = 0;
0
> a.length
4