Javascript: reverse an array of document.getElementByClassName - javascript

i have this variable that is:
var stars = this.parentNode.children
and its value is:
[span.rate, span.rate, span.rate.rated, span.rate.rated, span.rate.rated]
Now i want to reverse it, but if i try:
stars.reverse()
I obtain
Uncaught TypeError: stars.reverse is not a functionupdateRateStar # app.js:75(anonymous function) # app.js:98
I cannot understand why it works with an array like:
[1,2,3]
So if i try:
[1,2,3].reverse()
it works. Thus i cannot understand the problem

You can't call Array.prototype.reverse on NodeListCollection. Instead, you should use:
var stars = [].slice.call(stars, 0).reverse()

Use
var revArray = Array.prototype.slice.apply( a ).reverse()
The reason is that you have a NodeList and not an Array there. Both behave similarly in many cases, but the NodeList does not have the array methods.
With the
Array.prototype.slice.apply( a )
part, we convert the NodeList to an array, which can than be reversed.

It's a NodeList, which doesn't have a reverse. You can tell an object's prototype (like class in other languages) using:
Object.prototype.toString.call(stars)
Which returns [object NodeList]. An array would return [object Array].
If you own the prototypes (ie, this isn't a library), you can run:
NodeList.prototype.reverse = Array.prototype.reverse
then
stars.reverse()
Will work. Which is a lot more logical and pleasant to look at than constantly borrowing the method off Array.
Again, you own the prototypes here, be aware that it's on you to fix if the browser adds a prototype in future. You may prefer:
NodeList.prototype.sirkoReverse = Array.prototype.reverse
Which still reads nicely but is less likely to have conflicts.

It has different type , like NodeList and Array.
I suggest you to use this function:
var reverse = function(arr) {
var result = [];
for (var i = arr.length - 1;i !== 0;i--) {
result.push(arr[i]);
}
return result;
}
Second variation:
function xorSwapHalf(array)
{
var i = null;
var r = null;
var length = array.length;
for (i = 0; i < length / 2; i += 1)
{
r = length - 1 - i;
var left = array[i];
var right = array[r];
left ^= right;
right ^= left;
left ^= right;
array[i] = left;
array[r] = right;
}
return array;
}
There is another swap method called destructuring assignment: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
Destructuring assignment:
function destructuringSwap(array)
{
var left = null;
var right = null;
var length = array.length;
for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
{
[array[left], array[right]] = [array[right], array[left]];
}
return array;
}

Related

multidimensional array indexOf not working js

I'm trying to find an index of a number in a 2d array, but console gives out
Uncaught TypeError: block[((a * 10) + c)].indexOf is not a function
I think it has something to do with the way of accessing the array element, but can't seem to find the problem.
Here's the code.
var block = [];
var temp;
var del;
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(prompt("enter element number " + b + " of row number " + a));
console.log(temp);
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
// console.log(block[a*10+b]);
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
if(typeof(block[a][b]) == "number"){
for(var c = 0;c < 9;c++){
if(c != b){
del = block[a*10+c].indexOf(b);
block[a*10+c].splice(del,1);
}
}
}
}
}
You have a mix of data types assigned to the block array. When the user enters a value that is not numeric, you assign indeed a nested array to one of the block elements, but not so when the user enters a valid number.
From what I think you are doing (a Sudoko game?) this might be intended: the numbers are known values in the grid, the nested arrays represent a list of values that are still possible at that particular cell.
But then in the second part of your code, you should check in which of the two cases you are, as you only want to remove array elements if the value you are looking at is indeed an array. This test you can do with Array.isArray().
There are also some other issues in the second part of your script:
The expression block[a][b] is not consistent with how you have filled that array: it should be block[a*10+b] to be consistent.
the b in .indexOf(b) is wrong: you are not looking for that value, but for block[a*10+b].
the splice() is always executed, even if the indexOf returned -1. This leads to an undesired effect, because if the first argument to splice() is negative, the index really is counted from the end of the array, and still an element is removed from the array. This should not happen: you should only execute the splice if the indexOf result is non-negative.
Below I have put a working version, but in order to avoid the almost endless prompts, I have provided this snippet with a textarea where you can input the complete 9x9 grid in one go, and then press a button to start the execution of your code:
document.querySelector('button').onclick = function () {
var block = [];
var temp;
var del;
var text = document.querySelector('textarea').value.replace(/\s+/g, '');
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(text[a*9+b]); // <-- get char from text area
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
var num = block[a*10+b]; // <-- get content, fix the index issue
if(typeof num == "number"){
for(var c = 0;c < 9;c++){
if(c != b && Array.isArray(block[a*10+c])){ //<-- add array-test
del = block[a*10+c].indexOf(num); // <-- not b, but num
if (del > -1) // <-- only splice when found
block[a*10+c].splice(del,1);
}
}
}
}
}
document.querySelector('pre').textContent = 'block='+ JSON.stringify(block);
};
<textarea rows=9>
53..7....
6..195...
.98....6.
8...6...3
4..8.3..1
7...2...6
.6....28.
...419..5
....8..79
</textarea>
<button>Process</button>
<pre></pre>
Note that there are elements in block which remain null. I suppose you intended this: as you multiply a with 10, and only store 9 values per "row", there is always one index that remains untouched.
I haven't looked over your second for loop, but you can try applying similar logic there as in the snippet I've provided. The issue is that you need to create a temporary array inside the outer for loop over values of a (but NOT inside the inner, nested for loop over values of b). Inside the for loop for values of b, then, you need to push something into that temporary array (which I called temp). Then, outside of the b for loop, but before the next iteration of a, push that temporary array temp to the block array. In this way, you will generate a 2D array.
var block = [];
var del;
for(var a = 0; a < 9; a++) {
let temp = [];
for(var b = 0; b < 9; b++) {
let num = parseInt(prompt(`Enter element ${b} of row ${a}:`));
if (num > 0) {
temp.push(num);
} else {
// block[a*10+b] = [1,2,3,4,5,6,7,8,9];
temp.push(b);
}
}
block.push(temp);
}

indexOf() : is there a better way to implement this?

EDIT
Thank you guys, and i apologize for not being more specific in my question.
This code was written to check if a characters in the second string is in the first string. If so, it'll return true, otherwise a false.
So my code works, I know that much, but I am positive there's gotta be a better way to implement this.
Keep in mind this is a coding challenge from Freecodecamp's Javascript tree.
Here's my code:
function mutation(arr) {
var stringOne = arr[0].toLowerCase();
var stringTwo = arr[1].toLowerCase().split("");
var i = 0;
var truthyFalsy = true;
while (i < arr[1].length && truthyFalsy) {
truthyFalsy = stringOne.indexOf(stringTwo[i]) > -1;
i++
}
console.log(truthyFalsy);
}
mutation(["hello", "hey"]);
//mutation(["hello", "yep"]);
THere's gotta be a better way to do this. I recently learned about the map function, but not sure how to use that to implement this, and also just recently learned of an Array.prototype.every() function, which I am going to read tonight.
Suggestions? Thoughts?
the question is very vague. however what i understood from the code is that you need to check for string match between two strings.
Since you know its two strings, i'd just pass them as two parameters. additionally i'd change the while into a for statement and add a break/continue to avoid using variable get and set.
Notice that in the worst case its almost the same, but in the best case its half computation time.
mutation bestCase 14.84499999999997
mutation worstCase 7.694999999999993
bestCase: 5.595000000000027
worstCase: 7.199999999999989
// your function (to check performance difference)
function mutation(arr) {
var stringOne = arr[0].toLowerCase();
var stringTwo = arr[1].toLowerCase().split("");
var i = 0;
var truthyFalsy = true;
while (i < arr[1].length && truthyFalsy) {
truthyFalsy = stringOne.indexOf(stringTwo[i]) > -1;
i++
}
return truthyFalsy;
}
function hasMatch(base, check) {
var strOne = base.toLowerCase();
var strTwo = check.toLowerCase().split("");
var truthyFalsy = false;
// define both variables (i and l) before the loop condition in order to avoid getting the length property of the string multiple times.
for (var i = 0, l = strTwo.length; i < l; i++) {
var hasChar = strOne.indexOf(strTwo[i]) > -1;
if (hasChar) {
//if has Char, set true and break;
truthyFalsy = true;
break;
}
}
return truthyFalsy;
}
var baseCase = "hello";
var bestCaseStr = "hey";
var worstCaseStr = "yap";
//bestCase find match in first iteration
var bestCase = hasMatch("hello", bestCaseStr);
console.log(bestCase);
//worstCase loop over all of them.
var worstCase = hasMatch("hello", worstCaseStr);
console.log(worstCase);
// on your function
console.log('mutation bestCase', checkPerf(mutation, [baseCase, bestCaseStr]));
console.log('mutation worstCase', checkPerf(mutation, [baseCase, worstCaseStr]));
// simple performance check
console.log('bestCase:', checkPerf(hasMatch, baseCase, bestCaseStr));
console.log('worstCase:', checkPerf(hasMatch, baseCase, worstCaseStr));
function checkPerf(fn) {
var t1 = performance.now();
for (var i = 0; i < 10000; i++) {
fn(arguments[1], arguments[2]);
}
var t2 = performance.now();
return t2 - t1;
}

Find maximum variable using JavaScript

I have thirty variables which are all numbers:
h0 = 23
h1 = 27
h2 = 90
...
How do I find the largest variable?
The output here should be h2.
Assuming the variables are all named in consecutive order, you can add them to an array:
var values = [h0, h1, ....h29];
Then iterate over the array, compare the values and keep track of the index of the max value:
var maxIndex = 0;
for (var i = 1; i < values.length; i++) {
if (values[maxIndex] < values[i]) {
maxIndex = i;
}
}
maxIndex will now contain the index of the maximum value, and you could concat it with 'h', e.g.
console.log('h' + maxIndex);
Of course this approach makes a lot of assumptions and is fragile, but that's what you get when doing this kind of "meta-programming" (the variable name really should not be of any concern to anybody using the app).
Using an object would make it a bit better, at least concerning the variable name:
var values = {
h0: h0,
h1: h1,
...
};
var maxProp = '';
for (var prop in values) {
if (values[maxProp] < values[prop]) {
maxProp = prop;
}
}
Put them in an array instead of individual variables, then loop over the array.
To brute-force it, compare adjacent pairs of variables, then pairs of the results, until only one is left.
If you want the answer to be 'h2' instead of '90' (per comment), try using an Object instead of separate variables (or an array, as mentioned in other answers).
var find_max = {
'h0': 23,
'h1': 27,
'h2': 90,
// and so on
};
Then loop over the properties of the object to find the maximum.
var max;
for (var h in find_max) {
if (!max || (find_max[h] > find_max[max])) {
max = h;
}
}
You can probably improve this loop - try using hasOwnProperty to make sure that the only items in the loop are the ones you want.
You can get it easily by checking the window object using the bracket notation this way:
h0 = 11;
h1 = 24;
h2 = 28;
h3 = 345;
h4 = 1;
var i = 0;
var max = {
name: 'none',
val: 0
};
while (nextItem = window['h'+i]) {
if (nextItem > max.val) {
max = {
name: 'h'+i,
val: nextItem
};
};
i++;
};
document.getElementById('max').innerHTML = max.name + ' = ' + max.val;
<p>Maximum is: <span id="max"></span></p>
EDIT: I was too hasty in posting the below as it didn't take into account the real question. Since then there have been good answers presented so I'll just show a way that you can sort the variables in descending order. One way would be to place the numbers in an array of objects such that:
numArray = [{name:h0, value:23}, {name:h1, value:27}, {name:h2, value:90}];
And do:
numArray.sort(function(a, b){
return b.value - a.value;
});
According to this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
It looks pretty easy once you have the numbers in an array. You could call
Math.max.apply(null, numArray)
on the array, or, using ES6, you could say Math.max(...numArray) (... is the "spread" operator).
You could also do a sort:
numArray.sort(function(a, b){
return b - a;
});
Which would sort your array in descending order with the maximum number now at numArray[0].

Javascript get random variables from array but each just once

I´d like to get random variables from a javascript and prepend it to a div container like that:
// my Array
var thumbnailArray = new Array();
thumbnailArray[0] = 'contentarray1';
thumbnailArray[1] = 'contentarray2';
thumbnailArray[2] = 'contentarray3';
function thumbnailquery () {
for (var i = 0; i <= 3; i++) {
$('#myDiv').prepend(thumbnailArray[Math.floor(Math.random() * thumbnailArray.length)])
}
}
Now how can i ensure that each variable is only taken once but still randomly?
Thank You
Shuffle the array and pop of values instead
function shuffle(a) {
var c=a.length,t,r;
while (0 !== c) {
r = Math.floor(Math.random() * c);
c -= 1;t = a[c];a[c] = a[r];a[r] = t;
}
return a;
}
var thumbnailArray = [
'contentarray1',
'contentarray2',
'contentarray3'
];
shuffle( thumbnailArray );
thumbnailquery();
function thumbnailquery () {
for (var i = 0; i <= 3; i++) {
$('#myDiv').prepend( thumbnailArray.pop() );
}
}
FIDDLE
Copy the array. Then delete objects that you took from the copied array.
There's no elegant way to do it the way you describe. You could construct an array of "selected indices" and ensure that each random number didn't already exist in that array, but that has a worst case operating time of infinity so highly not recommended.
Your best bet would be to shuffle the array and then iterate over, selecting the elements in order (shuffled order).
See this Stack Overflow for a good way to shuffle an array in JavaScript
How can i shuffle an array in JavaScript?
Use splice() http://www.w3schools.com/jsref/jsref_splice.asp
randNum = thumbnailArray[Math.floor(Math.random() * thumbnailArray.length)]
$('#myDiv').prepend(splice(randNum,1))

Default values for objects in Javascript

Javascript Code
var a = {};
a.test += 1; //NaN
++a.test; //NaN
Instead
var a = {};
a.test = 0;
++a.test; //1
a.test += 1; //2
I wonder if there could be anyway that can make first code sample work the same as second, i.e without an explicit assignment to 0. As in assigning default value for any property of an object to 0 instead undefined. I'm trying to do this in node.js. So, no problem of cross browser things and old ECMA Specs.
var i;
for(i = 0; i<10; i++) {
if(a.test) {
++a.test;
} else {
a.test = 0;
++a.test;
}
//a.test = a.test || 0; (1)
//++a.test;
}
If it is possible then the inner if/else or the assignment statement(1) in the above code can be eliminated.
Javascript by default defines all new variables as undefined ( if not explicitly defined ) , which is different from Number object, which you are trying to define. So you should use smth like :
for (i=0; i<10; i++) {
a.test = a.test || 0;
++a.test
}
There's no way to do this for any arbitrary undefined property.
For a known property name, there is a way, but DO NOT USE IT !! 1
Object.prototype.test = 0;
This will give every object an implicit .test property with that value in it, and the first time you attempt to modify it the result will be stored in your own object:
> Object.prototype.test = 0
> a = {}
> a.test++
> a.test
1
1 Adding stuff to Object.prototype will break stuff, including for (key in obj)
This is where prototypes come in useful in javascript:
function Demo() {
}
Demo.prototype.x = 0;
> a = new Demo();
> a.x += 1
> a.x
1
> b = new Demo()
> b.x
0
In the first code, you can't. Any number added to a non-number or NaN value will always result in NaN
var a = {};
a.test += 1; // undefined + 1 = NaN
++a.test; // ++(undefined) = NaN
as for the inner if
for(i = 0; i<10; i++) {
a.test = a.test || 0; //use existing value, or if undefined, 0
++a.test; //increment
}
I think this would do:
var a = {"test":0};
a.test += 1;
Using standard JS it's not possible to do what you're asking. Unless you prototype I guess? But I'm not experienced with Prototype.And I don't think prototyping works with Node.js
The reason for this is because js is not a typed language (i.e. we only use var to declare a variable, not int a or string b), so in order to use the ++ operator, the variable needs to be given a type through assignment.
hope that helps

Categories

Resources