Related
I'm new to coding, still learning. My friend gave me a task to write a function that does return the 2nd highest number from an array, I've managed to do it using array.prototype.sort(). He said to replace "-" with a "<" or ">" to make the code more clear, that's where the problem started.
I'm using VCS on windows, and it's not working properly.
My friend uses a mac, everything works fine.
Tried it on jsfiddle, everything works fine.
const secondMax = (arr) => {
return arr.sort((a, b) => b - a)[1]; //does return the correct number after console.log()
};
const secondMax = (arr) => {
return arr.sort((a, b) => a < b)[1]; //does not
};
"a < b" should be sorting descending
"a > b" should be sorting ascending
But no matter which operator I use, the sorting fails and just returns the second number from the array
You're supposed to return a number, not a boolean. So the first is correct. The latter might work by chance on some javascript engines, but it's not guaranteed to.
sort sorts the array as String by default. If you pass a comparator, then it's a function which will depend on two parameters and return:
negative, if the first parameter is smaller than the second
0 if they are equal
positive, if the first parameter is greater than the second
Using a logical operator instead of the above is mistaken.
However, if you are interested in finding the second largest number, then it's better to do it using a cycle:
var largestNumbers = [];
var firstIndex = (arr[0] < arr[1]) ? 1 : 0;
largestNumbers.push(arr[firstIndex]);
largestNumbers.push(arr[1 - firstIndex]);
for (var i = 2; i < arr.length; i++) {
if (largestNumbers[1] < arr[i]) {
if (largestNumbers[0] < arr[i]) {
largestNumbers[1] = largestNumbers[0];
largestNumbers[0] = arr[i];
}
}
}
This is quicker than sorting an array and more importantly, it does not destroy your initial order just to find the second largest number.
Here is a simple example of an array that contains at least one other array. I want a way to find the index of an array, within an array. However, the code I have below does not work:
var arr = [1,2,[1,2]];
console.log(arr.indexOf([1,2])); //-> -1
for (var i = 0; i < arr.length; i++) {
if (arr[i] == [1,2])
return 'true' // does not return true
}
Intuitively, this should work but does not:
if ([1,2] == [1,2]) return 'true' // does not return true
Can someone explain why it does not work, and offer an alternative solution? Thanks!
No, but you can check it yourself:
var a = [1,2], b = [1,2];
a.length === b.length && a.every(function(x,y) { return x === b[y]; });
Arrays in JavaScript are compared by reference not by value. That is why
console.log([1,2] == [1,2])
returns false.
You need a custom function that could compare arrays. If you want to check only the first level of nesting you can use this code:
var compareArrays = function(a, b) {
if (a.length !== b.length) {
return false;
}
for (var i = 0, len = a.length; i < len; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
You are confusing the definitions of similar objects vs. the same object. Array.prototype.indexOf() compares the objects using the strict equality comparison algorithm. For (many) objects, this means an object reference comparison (i.e. the object's unique identifier, almost like its memory address).
In your example above, you are trying to treat two similar arrays as though they were the same array, and that's why it doesn't work. To do what you are trying to do, you need to use the same array. Like this:
var testArr = [1,2]; //a specific object with a specific reference
var arr = [1,2,testArr]; //the same array, not a different but similar array
console.log(arr.indexOf(testArr)); //returns 2
If you just want to know where arrays occur in the parent array, use Array.isArray():
...
if(Array.isArray(arr[i])) {
//do something
}
...
Hopefully that helps!
I am working on this problem from coderbytes:
Using the JavaScript language, have the function SecondGreatLow(arr) take the array of numbers stored in arr and return the second lowest and second greatest numbers, respectively, separated by a space. For example: if arr contains [7, 7, 12, 98, 106] the output should be 12 98. The array will not be empty and will contain at least 2 numbers. It can get tricky if there's just two numbers!
My solution works by removing the greatest and lowest values from the array and then using Math methods to return the second highest and lowest values.
However, when there are two or more instances of the greatest or lowest elements of the array, and their index positions are adjacent to each other, I believe only the first instance of this value is removed and the flow skips over the second instance.
Is there any way to have the loop run through the same index value twice in order to process adjacent greatest or lowest values?
Here are the two iterations of my solution which I've tested.. my original attempt using .forEach and my second using a for loop.. I've console.logged a situation in which the code works and in which it doesn't for each attempt.
I'm really new to all this, almost a month of learning in my free time so explaining yourself as if I'm really dumb is appreciated. Thanks!!!
// * First attempt - using .forEach method *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
lowestVal = g(); // store this value to be added back in for the secondGreatest function (in case there were only two digits in the arr argument)
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
// * Second attempt - using for loops *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
lowestVal = g();
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
I tried using the delete operator in order to keep the argument array length consistent (rather than shortening it with splice which I think allows the adjacent value to pass into the removed element's index position and not be processed in the next runthrough of the for loop or forEach method) but the Math.min/max.apply methods don't like having 'undefined' in the array argument.
Also if my code is looking ugly/annoying and makes you cringe then please take this opportunity to vent.. helps me learn to write code that doesn't piss people off ;)
** Solution Found **
Thank you for reminding me of the sort method!(function?) Here's what I ended up with:
function SecondGreatLow (arr) {
var secondLow = 0,
secondHigh = 0;
arr.sort(function(a,b){
return a-b;
});
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
secondLow = arr[i];
break;
}
}
for (var j = (arr.length-2); j >= 0; j--) {
if (arr[j] !== arr[j+1]) {
secondHigh = arr[j];
break;
}
}
return secondLow + " " + secondHigh;
}
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
What an awesome community.. I'll be back with more questions and hopefully I'll feel confident enough to even answer some questions in the near future. Thanks!
I feel like perhaps I'm missing something, but the challenge doesn't seem to include a requirement for removing items from the original array, so I don't see why you're modifying it in such a way. The requirements you provided simply state to return 'a b' where a is the second lowest, and b the second highest.
So, I would first recommend sorting the list. Since you know you're working at the upper and lower bounds, you don't have to iterate over anything (nor should you). Your test arrays are already sorted, but ensuring order will make your code more robust and able to handle other inputs. Check out the Arrays API for more details.
While it seems it may be beyond the scope of your problem, you may also want to look into sorting algorithms to learn more about how that all works, rather than relying solely on the API.
Once sorted, you should be able to easily compare inwards from the boundaries to get your second lowest and second highest values.
Also, you shouldn't need to utilize the Math API, simple inequality operators should do the trick (< and >).
EDIT: While I recommend working on the problem yourself, here is a simple solution to the problem. I place it here so if you get stuck you can reference this (and the associated comments) for guidance.
function SecondGreatLow(arr) {
var i;
var j;
var lowest;
var highest;
var secondLowest;
var secondHighest;
//Sort Array
arr.sort(function (a, b) {
return a - b;
});
//Get Bounds
//Since we sorted the array, and the default sort is in
//ascending lexicographical order, then we're guaranteed that
//our 'lowest' value is at index 0 and our 'highest' value is
//at index arr.length -1. Note that these values may be
//equal.
lowest = arr[0];
highest = arr[arr.length - 1];
//Search for second lowest.
for (i = 0; i < arr.length; i++) {
if (arr[i] > lowest) {
secondLowest = arr[i];
break;
}
}
//If we reach the end of the array, but didn't
//find a greater value, then, since the array is sorted,
//we're guaranteed that all values in the array are equal.
//Therefore, the required value comparisons have no meaning,
//and we return 'undefined'.
if (secondLowest === 'undefined') {
return 'undefined';
}
//Search for second highest, working backwards from the
//high end of the array until we reach our crossover point
//with the previous search. Either some value > arr[i] is the
//second highest, or arr[i] is, so there's no point in looking
//at values in the indices lower than i.
for (j = arr.length - 1; j >= i; j--) {
if (arr[j] < highest) {
secondHighest = arr[j];
break;
}
}
return secondLowest + ' ' + secondHighest;
}
var result = SecondGreatLow([3,3,4,5,4,6]);
console.log(result);
JSFiddle
You may create a priority queue limited by 2 elements, then feed it with all the array and pop the value, which would be the answer.
The trivial implementation would look like:
function UniqueNElementSortedQueue(length, comparison) {
this.length = length;
this.data = [];
this.comparison = comparison;
}
UniqueNElementSortedQueue.prototype.push = function(v) {
if (this.data.indexOf(v) > -1) {
return;
}
this.data.push(v);
this.data.sort(this.comparison);
this.data.length = this.length;
};
UniqueNElementSortedQueue.prototype.popIfN = function() {
if (this.data.length == this.length) {
return this.data[this.length - 1];
}
};
JSFiddle: http://jsfiddle.net/fmfv67xy/
The solution is O(N) (one might argue that I have sorting internally and they would be right :-)) by number of operations and O(N) by additional memory (where N is linear to the "next-lowest/greatest" index value)
As the description does not define what to return if it was not sufficient data fed - my implementation returns undefined.
Actually, let me turn my comment into an answer, since I think it always helps to also worry about performance:
Create 4 local variables:
largest and second_largest initialized to a number smaller than anything you'd expect in your array, or to the smallest possible value that your data-type can take on (-2^31 - 1)
smallest and second_smallest initialized to a number larger than anything you'd expect in your array, or the largest possible value for your data-type (2^31)
Loop over your array once:
If you find a number larger than largest, set second_largest to largest and largest to that number
If you find something smaller than largest but larger than second_largest, set second_largest to that number
If you find a number smaller than smallest, set second_smallest to smallest and smallest to that number
If you find something larger than smallest but smaller than second_smallest, set second_smallest to that number
When you're done with your loop, your answer is contained in second_largest and second_smallest
Given how small your arrays seem to be, you might not notice much of a performance difference between this answer and the other suggested ones, but I think it's a good habit to get into to always keep this concern in the back of your head for every line of code you write. In this answer, you process every array element exactly once (i.e. the algorithm runs in O(n)), whereas adding a sorting step leads to every element being processed multiple times in a general case (the best sorting algorithms (Timsort, for example) have an expected runtime of O(n log n)).
One thing to note:
#elclanrs mentioned a special case in his comment ([1, 1]), which according to the definition of the problem does not have a defined solution (multiple 1s all would be considered the largest number, so there is no second-largest). In this case, the algorithm above will still have second_largest and second_smallest set to their initial values.
So if I had:
A=['a','b','c'];
B=[];
for (var i = 0;i<7;i++){
B.push(A[i])
}
I would get
B=["a", "b", "c", undefined, undefined, undefined, undefined]
Instead I want
B= ["a","b","c"]
So I guess I would need something like
for (var i = 0;i<7;i++){
B.push(A[i] "unless A[i] is undefined. Then don't push anything")
}
How would I do this in Javascript?
Some ES magic
[1, 2, undefined].filter(Boolean)
will produce
[1, 2]
I think this is what you're searching:
Array#filter
B = A.filter(function (element) {
return element !== undefined;
});
That Code will make a duplicate of A filtering out all undefined elements.
Instead of iterating the array and pushing elements one at a time to a new array, it's usually more efficient to use the Array.filter function, like so:
var B = A.filter(function(item){ return item !== undefined; });
You may also want to be cautious about using undefined. This is easily confused with other constructs such as null or empty strings. If you use "!=" instead of "!==" in the function, that will filter out null or missing array items, as well as items which are set specifically as undefined.
You could add a safeguard like
A[i] && B.push(A[i])
which would only push if A1 returns a true, but that would also give false if A[i] would be 0
Example
I would suggest checking against null
if (A[i] != null) B.push(A[i])
Example
If you want to keep your first forloop as it is you can "clean" the array after by using filter. Like:
for (var i = 0; i < 7; i++) {
B.push(A[i])
}
var clean_B = B.filter(function (item) {
return item != null;
});
Example
An Object Oriented Aspect
=> After that code, clean your array :
B.clean();
Known that clean is an extension of array Class(OOP context)
Array.prototype.clean=function(){
var newArray = [];
for(var index = 0; index < this.length; index++) {
if(this[index]) {
newArray.push(this[index]);
}
}
return newArray;
};
You stated the answer to your question in your sort-of comment (use // at the end of the line next time, no need for inserting a comment in a string).
unless A[i] is undefined. Then don't push anything
Another way of putting it is, if A[i] is not undefined, then push.
When put like that, it directly translates into code.
if (A[i] !== undefined)
{
B.push(A[i]);
}
I elaborated on your comment to show you how easily you can figure these types of questions out with a little effort and time.
However, as others have stated, a much better way of ignoring undefined indices is to simply iterate to A.length, however this only works if there are no undefined values in-between the defined values (which is still likely the case).
Your problem would be solved by iterating from 1 to array.length, or by using a for in loop
If you're going to be dealing with arrays that could have undefined elements, for example [0, undefined, 4], then you'd need to check if the element is defined before you add it. if(a[i] !== undefined) will do the trick.
You would use an if statement
for (var i = 0; i < 7; i++){
if (A[i] !== undefined) {
B.push(A[i]);
}
}
You can have a compact function to filter out undefined values:
function compact(col) {
return col.filter(function(val) {
return val !== undefined;
});
}
console.log(compact([false,0,1,null,undefined])); // [false,0,1,null]
JS Bin Example
http://jsbin.com/mabowana/4/edit
initials:
A = ['a', 'b', 'c'];
B = [];
I think, the closest answer to OP's question would be this:
for (var i = 0; i < 7; i++) {
if (typeof A[i] !== "undefined") B.push(A[i]);
else continue;
}
or this one:
for (elem of A) if (typeof elem !== "undefined") B.push(elem); else continue;
Instead of hard coding i < 7, use i < A.length
for (var i = 0; i < A.length; i++){
B.push(A[i])
}
I have a little piece of code where an array is populated with arrays. At the end, I have this array with n member arrays. These sub-arrays always have the same number of members (2) of the same type (number). I need to know if these sub-arrays are all identical (same members, same position). This always returns false:
[2, 0] === [2, 0]
Off the top of my head, I can think of two ways to compare.
A loop in which I test (member[i][0] === member[i+1][0] && member[i][1] === member[i+1][1]).
The same loop, with this test: (member[i].toString() === member[i + 1].toString()).
I think I'm taking this the wrong way. What do you think?
FOR MY OWN THINKING, I think it is correct to use the loop to compare two array.
var isTheSame = true;
for (var i in array1) {
if (array1[i] !== array2[i]) isTheSame = false;
}
The loop above works, it will return false either any of the element different from type (three equal sign ===), or value, or length or even key.
The second suggestion of you should not work as exactly as the first one, because you convert it into a string already, what happen if array1[0] = "true" and array2[0] = true? It will return true, because now all of them are string, but you need the exact comparison right?
That's my own thinking, I hope it might help somehow.
Regards,
[x]
a=[2,0]; b=[2,0]; a.toString() == b.toString();
perhaps not the most efficient, but it seems to work, and I'm a strong proponent of the shorter and more readable solution.
note that xx3004's point about the type data lost when converting to string is something to think about, unless you know for sure that your arrays will be composed of the same data types.
You can use the below to get true or false on whether two one-dimensional arrays are identical. It needs to be recursive if you add dimensions, but this will suffice for what you're asking, I think.
function testArray(a, b) {
var aLen = a.length;
var bLen = b.length;
if (aLen === bLen) { // check for identical length first
for (var i = 0; i < aLen; i++) {
if (a[i] !== b[i]) {
return false; // members don't match
}
}
return true; // all members matched
}
return false; // not same length
}
http://jsfiddle.net/pgkUr/
This is what I came up with...
var arrayCompare = function(a, b) {
var aLength = a.length,
bLength = b.length;
if (aLength != bLength) {
return false;
}
for (var i = 0; i < aLength; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
Here's an easy way to compare the two arrays, building on your toString idea (even multidimensional ones will work):
function compareArrays(a1, a2) {
return JSON.stringify(a1) == JSON.stringify(a2);
}
compareArrays([[2,3],[7,4],[3,3]], [[2,3],[7,4],[3,3]]) // true
compareArrays([[2,3],[7,4],[3,3]], [[7,4],[2,3],[3,3]]) // false
If your willing to use Underscore, which is simply great tool (I value it as much or even higher than jQuery), you could simplify this code quite a lot. You don't need nesting during comparing arrays: I would flatten them beforehand. Then it's just one loop:
function cmpArrays( arr1, arr2 ){
var flat = _.zip( _.flatten( arr1 ), _.flatten( arr2 ) );
for( var elem in flat ){
if( flat[elem][0] !== flat[elem][1] ){
return false;
}
}
return true;
}
generalizing this to work with any number of arrays should be very simple as well.