I'm trying to resolve some exercises from hacker rank, and I'm struggling with the array map and filter methods.
This function supposes to search a value from queries array into strings array and when I use the map method it returns an array with undefined values, and when I use the filter method it returns an empty array.
var strings = ['ab','ab','bca','acb']
var queries = ['ab','abc','bc']
function matchingStrings(strings, queries) {
let retArr = []
for(let n = 0; n<queries.length; n++){
var equalelem = strings.filter(function(queries){queries[n] === [...strings]})
retArr.push(equalelem.length);
}
return equalelem
}
console.log(matchingStrings(strings, queries))
Looking only at your syntax, and without analyzing the algorithm, the following things should change:
Your filter function always returns undefined. Any function that doesn't have an explicit return statement returns undefined. Maybe you wanted to use an arrow function expression, which doesn't require a return statement if you omit the curly braces?
var equalelem = strings.filter((queries) => queries[n] === [...strings])
Inside the filter function, you seem to be comparing an element from list queries to a copy of list strings. Comparing an element of a list (in this case, a single string) to a list will never be true in your case.
The variable letArr doesn't seem to serve any purpose.
Related
I'm learning Javascript, so pardon any mistakes in how I phrase the question.
I am writing a chess program to practice and learn. Currently, I am trying to write a function to find the color of a piece with the position as the parameter. The relevant pieces of code are as follows. The first two work as they were designed to, but the last does not.
let allPieces = board.getElementsByClassName('piece');
This sets allPieces as an object with the key values the html elemnts representing each piece, both black and white.
const getPiecePosition = function(element) {
let position = window.getComputedStyle(element).getPropertyValue('grid-row-start');
let letterIndex = alphabet.findIndex(function(letter) {
return letter === position[0];
});
letterIndex += 1;
return [letterIndex, Number(position[1])];
}
This takes a parameter in the form of the allPieces object with a specific key and returns the position as an array with the column number first and the row number second. ex. [2,3].
const getPieceByPosition = function(position) {
let pce = Object.keys(allPieces).forEach(function(piece) {
if (getPiecePosition(allPieces[piece]) == position) {
return allPieces[piece].borderColor;
}
})
return pce;
}
This is the function I am having trouble with. The idea behind it is that it will take each key in the allPieces object and loop through them using forEach() into the getPiecePosition() function to compare it with the position entered as the parameter. Since only one piece can inhabit any tile at once, it should never return multiple values.
I honestly don't know where to start debugging this code, but I have been trying for about an hour. It always just returns undefined instead of a truthy value of any kind.
Your last function has a few issues:
getPiecePosition(allPieces[piece]) == position
Assuming position is an array, you're trying to compare an array with an array here using ==. However, since the two arrays are different references in memory, this will always give false, even if they contain the same elements:
console.log([2, 3] == [2, 3]); // false
You're trying to return from the callback of .forEach(). This won't achieve what you want, as return will jump out of the .forEach callback function, not your outer getPieceByPosition() function. This leads me to your final issue:
The .forEach() method doesn't return anything. That is, it doesn't evaluate to a value once it is called. This means that let pce will always be undefined since you're trying to set it to the return value of .forEach(). This, in contrast to let letterIndex, is different, as letterIndex is set to the return value of .findIndex(), which does have a return value and is determined by the function you pass it.
One additional thing you can fix up is the use of Object.keys(allPieces). While this works, it's not the best approach for looping over your elements. Ideally, you would be able to do allPieces.forEach() to loop over all your elements. However, since allPieces is a HTMLCollection, you won't be able to do that. Instead, you can use a regular for loop or a for..of loop to loop over the values in your HTMLCollection.
Alternatively, there is a way to make allPieces.forEach() work.
Instead of using board.getElementsByClassName('piece');, you can use the method .querySelectorAll('.piece'), which will give you a NodeList. Unlike a HTMLCollection, a NodeList allows you to use .forEach() on it to loop through its elements.
The return type of getElementsByClassName HTMLCollection Object. You should't use Object.keys to loop through each of 'piece' element. Insted, use the follow.
for(var i = 0 ; i < allPieces.length ; i++){
var piece = allPieces[i];
... // and, do whatever with the getPiecePosition(piece)
}
I am new to JS and was trying to learn how to properly work with indexOf in JS, that is, if you look at the code below:
var sandwiches = ['turkey', 'ham', 'turkey', 'tuna', 'pb&j', 'ham', 'turkey', 'tuna'];
var deduped = sandwiches.filter(function (sandwich, index) {
return sandwiches.indexOf(sandwich) === index;
});
// Logs ["turkey", "ham", "tuna", "pb&j"]
console.log(deduped);
I am trying to remove duplicates but wanted to ask two questions. Firstly, in here return sandwiches.indexOf(sandwich) === index; why we need to use "== index;". Secondly, since indexOf returns index like 0, 1 or 2 ... then why when we console.log(deduped) we get array of names instead of array of indexes. Hope you got my points
You use a method of Javascript Array that is filter, this method take a function that returns a boolean.
The function filter returns a new Array based on the function passed applied to each entry.
If the function return true, then the entry is included in the new Array, otherwise is discarded.
As the functions check the indexOf an entry to be the current index is true for the first occurrency of the entry.
All the duplications will fail the expression as they are not the first index found by indexOf, so they are discarded.
since the logic is to remove the duplicates from the array,
in your example, you have "turkey" as duplicates.
the "turkey" exists in position 0,2,6
so whenever you call indexOf("turkey") always returns 0 because the indexOf function returns the first occurrence of a substring.
so for the elements in position 2 & 6 the condition fails. then it won't return that element.
That is how the filter works in javascript. it evaluates the condition and returns true or false that indicates whether an element to be included in the new array or not, in your example the condition is return sandwiches.indexOf(sandwich) === index;
Perhaps the basic logic is easier to see at a glance if you use arrow notation:
const deduped = myArray => myArray.filter((x, i) => myArray.indexOf(x) === i);
The key point is that indexOf returns the index of the first occurrence of x. For that occurrence the result of the comparison will be true hence the element will be retained by the filter. For any subsequent occurrence the comparison will be false and the filter will reject it.
Difference between === (identity) and == (equality): if type of compared values are different then === will return false, while == will try to convert values to the same type. So, in cases where you compare some values with known types it is better to use ===. (http://www.c-point.com/javascript_tutorial/jsgrpComparison.htm)
You get as result an array of names instead of array of indexes because Array.filter do not change the values, but only filter them. The filter function in your case is return sandwiches.indexOf(sandwich) === index; which return true or false. If you want get the indexes of your items after deduplication, then use map after filter:
a.filter(...).map(function(item, idx) {return idx;})
#Dikens, indexOf gives the index of the element if found. And if the element is not found then it returns -1.
In your case you are filtering the array and storing the values in the deduped. That's why it is showing an array.
If you console the indexOf in the filter function then it will log the index of the element.
For example :
var deduped = sandwiches.filter(function (sandwich, index) {
console.log(sandwiches.indexOf(sandwich));
return sandwiches.indexOf(sandwich) === index;
});
As the title mentions, I tried rewriting getElementByClassName as a personal exercise, but am running into some unexpected behavior with my recursive return of results.
Document.prototype.getElementsByClassNameExercise = function(className, tempElement){
var currentElement = (tempElement || document),
children = currentElement.childNodes,
results = [],
classes = [];
// Loop through children of said element
for(var i =0;i<children.length;i++){
if(children[i].className && children[i].className !== '') {
classes = children[i].className.split(' ');
// Important to note, forEach is not ie8 safe.
classes.forEach(function(singleClass){
if(singleClass === className) {
results.push(children[i]);
}
});
}
results.concat(Document.prototype.getElementsByClassNameExercise.call(this, className, children[i]));
}
return results;
}
I attempted this on my homepage, and it appears to successfully parse all DOM elements and find the className... but the return/ results.concat(results) step seem to fail. :/
Any takers can see what I am missing? :)
Your Problem
You're not missing much.
concat() returns a new array, as explained in its MDN article:
Summary
Returns a new array comprised of this array joined with other array(s) and/or value(s).
Description
[...] concat does not alter this or any of the arrays provided as arguments but instead returns a shallow copy that contains copies of the same elements combined from the original arrays.
In case of doubt, you can also always refer to the ECMA-262 specs if the MDN isn't enough, section 15.4.4.4:
When the concat method is called with zero or more arguments item1, item2, etc., it returns an array containing the array elements of the object followed by the array elements of each argument in order.
Solution
You need to-reassign the results variable.
Change this line:
results.concat(Document.prototype.getElementsByClassNameExercise.call(this, className, children[i]));
to:
results = results.concat(Document.prototype.getElementsByClassNameExercise.call(this, className, children[i]));
I have the following javascript code:
var objectArray = [];
var allInputObjects = [];
var allSelectObjects = [];
var allTextAreaObjects = [];
//following returns 3 objects
allInputObjects = document.getElementById("divPage0").getElementsByTagName("INPUT");
//following returns 1 object
allSelectObjects = document.getElementById("divPage1").getElementsByTagName("SELECT");
//following returns 0 objects
allTextAreaObjects = document.getElementById("divPage2").getElementsByTagName("TEXTAREA");
//and following statement does not work
objectArray = allInputObjects.concat(allSelectObjects);
And my problem is that the last line is throwing an error.
I tried the above code in Firefox and it says allInputObjects.concat is not a function.
Any clues, I believe the script is not treating allInputObjects as an Array!
Any help will be appreciated.
getElementsByTagName returns a NodeList, which is similar to an Array except that it does not support all those prototype functions.
To seamlessly convert such an array-like object into an Array, use:
var arr = Array.prototype.slice.call(somenodelist, 0);
arr will almost be identical, except that it now has the Array prototype functions supported, like concat.
What the function actually does is returning a partial Array containing the elements of somenodelist, to be precise everything from index 0 and after. Obviously, this are just all elements, therefore this is a trick to convert array-like objects into real Arrays.
Why do you think that allSelectObjects is an array?
It is initially assigned to an empty array, sure. But then it's overwritten by the getElementsByTagName call on line 6(ish). Variables in Javascript are not strongly typed, so the initial assignment does not force later assignments to also be arrays.
I suspect you'll have some type of NodeList or similar, rather than an array, in the variable when you invoke the final line. (That's what's returned by the method in Firefox, at least.)
As Andezej has pointed out, you're not dealing with an array here, you're dealing with a kind of node list.
Here's one way you can create an array to work with based on the result of getElementsByTagName
var tags = obj.getElementsByTagName('input');
for (var j=0;j<tags.length;j++) {
resultArray.push(tags[j]);
}
Just another funny way to convert your NodeList to an array:
var tags = obj.getElementsByTagName('input'),
tags2Arr = (
function toArr(i){
return i ? toArr(i-1).concat(tags[i]) : [tags[0]];
}(tags.length-1)
);
Now if you add a method to the Array.prototype:
Array.prototype.clone = function() {
var arr = this;
return (function clone(i){
return i ? clone(i-1).concat(arr[i]) : [arr[0]];
})(this.length-1);
};
You can convert a NodeList to an array, using this oneliner:
Array.prototype.clone.call(obj.getElementsByTagName('input'));
I'm not a javascript guru. I've got the following code below:
var aCookieValues = sCookieContentString.split('&'); // split out each set of key/value pairs
var aCookieNameValuePairs = aCookieValues.split('='); // return an array of each key/value
What I'm trying to do is split the first string via & and then create another array that takes the first array and splits it further via the = character that exists in every value in the aCookieValues array
I get the error aCookieValues.split is not a function.
I've seen an example that basically does the same thing but the second time this guy is using a loop:
(http://seattlesoftware.wordpress.com/2008/01/16/javascript-query-string/)
// '&' seperates key/value pairs
var pairs = querystring.split("&");
// Load the key/values of the return collection
for (var i = 0; i < pairs.length; i++) {
var keyValuePair = pairs[i].split("=");
queryStringDictionary[keyValuePair[0]] = keyValuePair[1];
}
Ultimately what I'm trying to achieve here is a final dictionary with key/value pairs based off the '=' split. I'm simply trying to split up a cookie's values and shove it into a nice dictionary so I can then get certain values out of that dictionary later on.
You are getting this error because aCookieValues is an array, and it does not have a split method. You would need to call the split method on each element of aCookieValues:
var aCookieValues = sCookieContentString.split('&');
for (var i = 0; i < aCookieValues.length; i++) {
var aCookieNameValuePairs = aCookieValues[i].split('=');
// Handle aCookieNameValuePairs[0] as the key
// Handle aCookieNameValuePairs[1] as the value
}
To shove everything in your nice dictionary, simply declare it before the for loop: var myDict = {}, and then put the following after the split('=') call:
myDict[aCookieNameValuePairs[0]] = aCookieNameValuePairs[1];
EDIT: Which, after reading your question properly, is the same method used in the code snippet you supplied. I hope at least this explains how that works :)
In your second line you are attempting to call split() on an array, when it is a function defined on strings.
Example:
"a=1&b=2&c=3".split('&') returns an array ['a=1','b=2','c=3']
Your code would then call split on the array:
['a=1','b=2','c=3'].split('=')
But that function doesn't exist. It seems like your goal is to split each string in the array, so the example you gave in the question seems appropriate - loop through each element and split it.
split operates on a string. You're trying to split aCookieValues, which is an array. The example you cite is looping through the array, and then splitting each element as a string.
Just for fun, one way to deal with this would be to use a map function, which performs an action on each element of an array, and emits an array as a result. If you make a generic map function available to all your arrays, like this:
if (!Array.prototype.map) { // don't step on anyone's toes
Array.prototype.map = function( f ) {
var result = [];
var aLen = this.length;
for( x = 0 ; x < aLen ; x++ ) {
result.push( f(this[x]) );
}
return result;
};
};
...you can call it as a method on your array directly. Thus:
​yourstring = 'x=3&y=4&zed=blah&something=nothing';
dictionary = yourstring.split('&').map( function(a){ return a.split('='); } );
dictionary will now be a nice clean array of (arrays of) name/value pairs, like this:
[["x", "3"], ["y", "4"], ["zed", "blahblah"], ["something", "nothing"]]
If your use case becomes complex, an approach like this can be a nice abstraction. Of course, you can arrange these data in other structures if needed, either by playing with a function passed into map, or processing in a separate pass.