I am having seemingly inconsistent results trying to sort arrays of strings using the sort method, both with and without passing sort() a function:
function sortThese(strArr) {
var words = [];
for (var i = 0; i < strArr.length; i++) {
words.push(String(strArr[i].length + "," + strArr[i]));
}
words = words.sort();
console.log(words);
}
sortThese(["battt","catt","mat"]);
sortThese(["as","assssvvvvt","affggg"]);
in this case my results are:
["3,mat", "4,catt", "5,battt"]
["10,assssvvvvt", "2,as", "6,affggg"]
so it appears that the method is seeing my "10" as "1."
How do I make it see the value as 10?
Futher, if I pass sort this function:
words = words.sort(function(a,b){
return a - b;
});
the result is not sorted:
["4,catt", "5,battt", "3,mat"]
["2,as", "10,assssvvvvt", "6,affggg"]
I would love some clarification regarding how I should expect the sort method to behave, and how to make it sort these strings! Thank you
The "10" comes before "3", because you've made it into a string (ie, "1" comes before "3"). If you want to sort by length, you could use the sort function to compare lengths:
words.sort(function(a, b) { return a.length > b.length; });
Fiddle
You could also create a nested object array, in order to hold a number and the string in each item:
var words = strArr.map(function(str) {
return { len: str.length, str: str }
});
words.sort(function(a, b) { return a.len > b.len; });
(Of course, this second approach is unnecessary for the example case, but may be useful in other cases.)
Fiddle
Sorting strings always uses alphabetical order, not numerical. Just imagine 0=A 1=B 2=C and so on. Letters are not connected as numbers, so everything starting with 1 (respective B) will be sorted before 2 (respective C). That's why you get 10,2,6. For 10, only the 1 counts.
Using a custom sort function is a good way to achieve this. But you cannot subtract strings. Extract the integer before and compare afterwards:
words = words.sort(function(a,b){
//parseInt will read only the first numbers and discard the letters after this
return parseInt(a) - parseInt(b)
});
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.
I have 2 multidimensional arrays:
[[230.0], [10.0], [12.0]]
[[50.0], [60.0], [89.0]]
And am trying to sum each element together and keep the same array structure. So it should look like:
[[280.0], [70.0], [101.0]]
I tried this:
var sum = array1.map(function (num, index) {
return num + array2[index];
});
But I get this:
[23050, 1060, 1289]
Any help would be appreciated. Thanks.
The code, you use, takes only a single level, without respecting nested arrays. By taking na array with only one element without an index of the inner array and using an operator, like +, the prototype function toString is invoced and a joined string (the single element as string, without , as separator) is returned and added. The result is a string , not the result of a numerical operation with +.
You could take a recursive approach and check if the value is an array, then call the function again with the nested element.
function sum(a, b) {
return a.map((v, i) => Array.isArray(v) ? sum(v, b[i]) : v + b[i]);
}
console.log(sum([[230], [10], [12]], [[50], [60], [89]]))
Make it like this
var sum = array1.map(function (num, index) {
return parseInt(num) + parseInt(array2[index]);
});
You should have to make parseInt or parseFloat so it can convert string with them
STEPS
Iterate through every number in the array (array length).
Sum the objects of the same index in both of the arrays.
Push the sum into another array for the result. Use parseFloat if the input is string.
(Optional) use .toFixed(1) to set decimal place to have 1 digit.
const arr1 = [[230.0], [10.0], [12.0]]
const arr2 = [[50.0], [60.0], [89.0]]
let sum = []
for (let i = 0; i < arr1.length; i++){ // Assume arr1 and arr2 have the same size
let eachSum = parseFloat(arr1[i]) + parseFloat(arr2[i])
sum.push([eachSum.toFixed(1)])
}
console.log(sum)
You are trying to add two arrays structures inside the map function.
so one solution so you can see what is happening is this...
array1.map((a,i) => a[0] + array2[i][0])
screenshot from the console...
Inside map fn you should:
return parseInt(num) + parseInt(array2[index]);
This is happening because when you are trying to add them, these variable are arrays and not integers. So they are evaluated as strings and concatenated.
This question already has answers here:
Fast stable sorting algorithm implementation in javascript
(16 answers)
Closed 6 years ago.
Here is my jsFiddle:
//Change this variable to change the number of players sorted
var numberOfPlayers = 15;
var teams = [];
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(var a=0; a<numberOfPlayers; a++){
updateStandings();
teams.push(new Team(alphabet.charAt(a)));
}
console.log("Teams:");
for(var x=0; x<teams.length; x++){
console.log(teams[x].name);
}
//Functions and such
function updateStandings(){
teams.sort(function(a, b) {
if(a.score == b.score){
if(a.tiebreak == b.tiebreak){
return teams.indexOf(a)-teams.indexOf(b);
}else{
return b.tiebreak-a.tiebreak;
}
}else{
return b.score-a.score;
}
});
}
function Team(name){
this.name = name;
this.score = 0;
this.tiebreak = 0;
}
I assumed the problem was that javascript sorting was unstable, and changed my compare function, but it still does not work.
The generic approach to stable sorting in JS is as follows:
function stable_sort(array, sortfunc) {
function _sortfunc(a, b) { return sortfunc(array[a], array[b]) || a - b; }
return array.map((e, i) => i) . sort(_sortfunc) . map(i => array[i]);
}
What this actually does is to sort a list of indices. Then it maps the sorted list of indices back to the original array. The sort function is rewritten to compare the values in the array at those indices, and if they are equal then fall back to a comparison of indices themselves.
This approach avoids the problem in your code which is that it is doing indexOf look-ups into an array which is the middle of being sorted.
This question could be informative.
According to the documentation, sort method is not required to be stable: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort In some browsers it is stable, in some not.
You do need to change the compare function, but not in the way that you tried. The reason is that you compare
return teams.indexOf(a)-teams.indexOf(b);
in the current array. It means that if the order of a and b has changed on the previous steps, your sorting routine will preserve this new order, not the one that these elements had in the beginning.
There are different ways to solve it. For example, you can create a copy of the array before sorting and execute indexOf on this copy. It will preserve the order that elements had had before sorting started.
But if your know that order in advance, you can also use this knowledge. For example, if before sorting the teams was sorted by their names, you can compare names as strings instead of positions in the array, it would be much more efficient than the first option.
Because JS' sorting is typically unstable. From ยง22.1.3.24 of the spec:
The elements of this array are sorted. The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order).
Your teams are created with identical properties except their name, so the line actually performing the sort is:
return teams.indexOf(a)-teams.indexOf(b);
Because you're calling indexOf, it searches for the item (and its index) each repetition of the sort. Sorting mutates the array (from MDN: it "sorts the elements of an array in place and returns the array").
You are searching for the item within the same array you are sorting, so the index may change on each iteration. Done correctly (relatively speaking), you could produce a never-ending sort with that.
For example:
const data = [1, 3, 2, 4];
let reps = 0;
data.sort((a, b) => {
console.log(data);
const ia = data.indexOf(a), ib = data.indexOf(b);
if (ia === ib || reps > 50) {
return 0;
} else if (ia < ib) {
return 1;
} else if (ib < ia) {
return -1;
}
});
I have an idea on how to improve some code but that would require using arrays and these arrays have to have a certain sorting order.
I have a given array: var p = ['skewX', 'translateY', 'scale', 'rotateX'];.
I need these strings sorted inside the array on a pattern:
0 translate, 1 rotate, 2 skew, 3 scale
or
0 ordered translations, 1 ordered rotations, 2 ordered skews, 3 ordered scales
where
index and string
Question: is it possible to sort these arrays based on this pattern?
Thanks so much.
The callback function doesn't sort itself. It just needs to compare any two items that are passed to it. So you have to write the logic that translates strings starting with 'translate' come before strings starting with 'rotate'.
// Very simple, rudimentary function to translate a type to number. Improve at will.
function typeIndex(x) {
if (x.indexOf('translate') > -1) return 0;
if (x.indexOf('rotate') > -1) return 1;
if (x.indexOf('skew') > -1) return 2;
if (x.indexOf('scale') > -1) return 3;
return 1000; // Unknown
}
var p = ['skewX', 'rotateY', 'rotateZ', 'translateY', 'scale', 'rotateX', 'ordered skewing'];
// Sort array using callback;
p.sort(function(a, b){
// First compare the difference based on type.
var result = typeIndex(a) - typeIndex(b);
// If the difference is 0, they are of the same type. Compare the whole string.
if (result == 0)
result = a.localeCompare(b);
return result;
});
console.log(p);
The sort function can be based on the words you want to sort on in sequence, provided they have unique sequences of characters:
var p = ['skewX', 'translateY', 'scale', 'rotateX'];
p.sort(function(a, b) {
var order = 'transrotaskewscal';
return order.indexOf(a.slice(0,4)) - order.indexOf(b.slice(0,4));
});
document.write(p);
I have got an array of the form:
['32 68', '56 78', '77 99']
I want to o/p another array which will contain the sum of each element in the index using JavaScript (NodeJS). Something like,
['100', '134', '176']
I tried to use .split("") but the double integer number again gets separated as separate digits. Is there any other way to solve this? Please not that, the i/p can be single digit number or double digit.
You'll want to get each item, split on a space (if exists) then add up the corresponding split. Something like this:
var origValues = ['32 68', '56 78', '77 99', '7'];
var addedValues = origValues.map(function(value) {
return value.split(' ')
.map(function(sArray) {
return parseInt(sArray);
})
.reduce(function(a, b) {
return a + b;
});
});
document.write(JSON.stringify(addedValues));
Note that this above example handles the case where you have a single digit inside your array value as well.
To provide some explanation as to what is happening...
You start off taking your original array and you are mapping a function on to each value which is what is passed into that function.
Inside that function, I am splitting the value by a space which will give me an array of (possibly) two values.
I then apply the map function again onto the array and parse each value in the array to an integer.
Last, I reduce the integer array with a summation function. Reduce applies an accumulator function to each item in the array from left to right so you will add up all your values. This result is returned all the way back up so you get your new array with your answers.
Kind of what it looks like in "drawing" form:
Start: origValues = ['32 68', '56 78', '77 99', '7']
Apply map (this will track one value): value = '32 68'
Apply the split: ['32', '68']
Map the parse integer function (I'm going to track both values): [32, 68]
Reduce: 32 + 68 = 100
I don't have time for an explanation (sorry) but, split + reduce will do it.
var arr = ['32 68', '56 78', '77 99'];
var sumArray = arr.map(function (s) {
return s.split(' ').reduce(function (a, b) {
return parseInt(a, 10) + parseInt(b);
});
});
document.write(JSON.stringify(sumArray));
You don't actually need map or anything. For each string we can .split, Numberify, and add.
secondArray[value] =
Number((firstArray[value].split(" "))[0]) +
Number((firstArray[value].split(" "))[1]);
Modifying this and turning this into a for loop, we get:
var arr2 = [];
for(var i = 0; i < arr.length; i ++){
arr2.push(
Number((arr[i].split(" "))[0]) +
Number((arr[i].split(" "))[1]));
}
arr = arr2;