check if elements in array are consecutive --- javascript - javascript

I have an array as
arr = [1,2,3,4,6,7,8,9]
Now I want to check if the values in the array are consecutive.
Being more specific, I want this
First Check gives first and second element are consecutive and the next element is not consecutive then the algo must return the first element from where the consecutive number started
Like
First Check will give 1
Second Check will give 6
and so on...
Please help
Thanks in advance

/**
* Given an array of number, group algebraic sequences with d=1
* [1,2,5,4,8,11,14,13,12] => [[1,2],[4,5],[8],[11,12,13,14]]
*/
import {reduce, last} from 'lodash/fp';
export const groupSequences = (array) => (
reduce((result, value, index, collection) => {
if (value - collection[index - 1] === 1) {
const group = last(result);
group.push(value);
} else {
result.push([value]);
}
return result;
}, [])(array)
);

/**
* Given an array of number, group algebraic sequences with d=1
* [1,2,3,4,5,6] => true
* [1,2,4,5,6] => false
*/
const differenceAry = arr.slice(1).map(function(n, i) { return n - arr[i]; })
const isDifference= differenceAry.every(value => value == 1)
console.log(isDifference);

One sidenote is that you want to call it multiple times, so each call should know which array it's working on and what the previous offset in that array was. One thing you can do is to extend the native Array object. [Demo]
Array.prototype.nextCons = (function () {
var offset = 0; // remember the last offset
return function () {
var start = offset, len = this.length;
for (var i = start + 1; i < len; i++) {
if (this[i] !== this[i-1] + 1) {
break;
}
}
offset = i;
return this[start];
};
})();
Usage
var arr = [1,2,3,4,6,8,9];
arr.nextCons(); // 1
arr.nextCons(); // 6
arr.nextCons();​ // 8

Check if all numbers in array are consecutives:
Updated March 2022
const allConsecutives = (arr) =>{
if(arr.some(n=> typeof n !== "number" || Number.isNaN(n))) return false;
return arr.every((num, i)=> arr[i+1]-num === 1 || arr[i+1] === undefined)
}

pseudo code:
int count = 0
for i = 0 to array.length - 2
if {array[i + 1] - array[i] = 1 then
count+=1
return i
else count=0}

const array1 = [1,2,3];
const sum = array1.reduce((accumulator, currentValue) =>{
return accumulator + currentValue;
});
const max = Math.max(...array1);
maximum = max
if(sum == maximum * (maximum+1) /2) {
console.log(true);
} else {
console.log(false);
}

Related

Trying to solve sliding window median problem in leetcode

I am working on LeetCode code challenge 480. Sliding Window Median:
You are given an integer array nums and an integer k. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the median array for each window in the original array. Answers within 10-5 of the actual value will be accepted.
I submitted my code, but it fails on test cases. I suspect that there is a problem in this part of my code:
const medianSlidingWindow = (array, window) => {
let start = 0;
let end = window - 1;
const min = new MinHeap(array);
const max = new MaxHeap(array);
const insert = (index) => {
if(max.size === 0){
max.push(index);
return;
}
(array[index] >= max.peak) ? min.push(index) : max.push(index);
balance();
}
const balance = () => {
if(Math.abs(max.size - min.size) >= 2){
const returned = (max.size > min.size) ? max.pop() : min.pop();
(max.size > min.size) ? min.push(returned) : max.push(returned);
}
}
const remove = (index) => {
(max.has(index)) ? max.pop(index, true) : min.pop(index, true);
balance();
}
const next = () => {
remove(start++);
insert(++end);
}
const getMedian = () => {
if(window % 2 === 0) return (max.peak + min.peak)/2;
return (max.size > min.size) ? max.peak : min.peak;
}
for(let i = 0; i <= end; i++){
insert(i);
}
const ret = [];
while(end < array.length){
ret.push(getMedian());
next();
}
return ret;
}
Here is the full code:
class MaxHeap{
#array = [];
#size = 0;
#reference = [];
#map = new Map();
constructor(reference = []){
this.#reference = reference;
}
get size(){
return this.#size;
}
/* Debug */
get array(){
return this.#array;
}
get peak(){
return this.get(0);
}
get(index){
if(index === null || index < 0 || index >= this.#array.length) return null;
return this.#reference[this.#array[index]];
}
has(indexReference){
return this.#map.has(indexReference);
}
swap(indexA, indexB){
let temp = this.#map.get(this.#array[indexA]);
this.#map.set(this.#array[indexA], indexB);
this.#map.set(this.#array[indexB], temp);
[this.#array[indexA], this.#array[indexB]] = [this.#array[indexB], this.#array[indexA]];
}
sink(index){
let currentIndex = index;
let greterChild;
while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 1 : 2*currentIndex + 2) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
this.swap(currentIndex, greterChild);
currentIndex = greterChild;
}
}
bubble(index){
let currentIndex = index;
let parent;
while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
this.swap(currentIndex, parent);
currentIndex = parent;
}
}
push(...char){
if(char[0].constructor === Array) char = char.flat();
for(let i = 0; i < char.length; i++){
this.#array.push(char[i]);
this.#map.set(char[i], this.#array.length - 1)
this.bubble(this.#array.length - 1);
this.#size++;
}
}
pop(index = 0, fromReference = false){
const ret = (fromReference) ? index :this.#array[index];
if(fromReference) index = this.#map.get(index);
this.swap(index, this.#array.length - 1);
this.#map.delete(ret);
this.#array.pop();
this.sink(index);
this.#size--;
return ret;
}
}
class MinHeap extends MaxHeap{
constructor(reference = []){
super(reference);
}
get size(){
return super.size;
}
get peak(){
return super.peak;
}
/* Debug */
get array(){
return super.array;
}
bubble(index){
let currentIndex = index;
let parent;
while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
this.swap(currentIndex, parent);
currentIndex = parent;
}
}
sink(index){
let currentIndex = index;
let lesserChild;
while((this.get(lesserChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 2 : 2*currentIndex + 1) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
this.swap(currentIndex, lesserChild);
currentIndex = lesserChild;
}
}
}
const medianSlidingWindow = (array, window) => {
let start = 0;
let end = window - 1;
const min = new MinHeap(array);
const max = new MaxHeap(array);
const insert = (index) => {
if(max.size === 0){
max.push(index);
return;
}
(array[index] >= max.peak) ? min.push(index) : max.push(index);
balance();
}
const balance = () => {
if(Math.abs(max.size - min.size) >= 2){
const returned = (max.size > min.size) ? max.pop() : min.pop();
(max.size > min.size) ? min.push(returned) : max.push(returned);
}
}
const remove = (index) => {
(max.has(index)) ? max.pop(index, true) : min.pop(index, true);
balance();
}
const next = () => {
remove(start++);
insert(++end);
}
const getMedian = () => {
if(window % 2 === 0) return (max.peak + min.peak)/2;
return (max.size > min.size) ? max.peak : min.peak;
}
for(let i = 0; i <= end; i++){
insert(i);
}
const ret = [];
while(end < array.length){
ret.push(getMedian());
next();
}
return ret;
}
What went wrong:
On the 30th testcase of the problem (link: https://leetcode.com/problems/sliding-window-median/submissions/859041571/), it resolves to a wrong answer but when I pick one of the windows that resolves to a wrong answer it gives me a correct answer. I'm currently confused because two of the heaps are fairly balanced (as one heap doesn't exceed above one element) and I've tested my heap that both seem to work perfectly. It will be very helpful if somebody helps me.
Link to SO questions I've followed:
How to implement a Median-heap
There are these problems in your heap implementation:
The get function will return null when an out of range index is given, which means the while condition in your sink method could sometimes choose an non-existing child (when there is only one child). Note that a numerical comparison with null will treat that null as 0, and depending of the sign of the value you compare it with can give false or true.
For example, your code fails this test case for that reason:
nums=[1,2,3,4]
k=4
You can fix this by returning undefined instead of null. Then also make sure that the false side of the comparison operator is the one with +1 (choosing the left child), while the true side takes the other child.
The pop method, when called with true for the second argument, does not guarantee to restore the heap property. It takes care of sinking the value at the given index, but does not consider the case where this value should actually bubble up!
For example, your code fails this test case for that reason:
nums=[10,6,5,2,3,0,8,1,4,12,7,13,11,9]
k=11
Here is a simplified example where I depict a min-heap with the referenced values:
5
/ \
8 6
/ \ /
10 12 7
If the node with value 10 is to be removed, the swap action will give this min-heap (which is correct):
5
/ \
8 6
/ \ /
7 12 10
And then your code calls sink on that node with value 7. It is clear that there is nothing to sink here, but instead that 7 should bubble up and swap with 8. Your code must foresee both scenarios: sift or bubble.
If you fix those two issues in your heap implementation, it will work.
I provide here the literal changes you have to make:
In the get method, replace return null with return undefined (or omit the explicit value)
In the MaxHeap sink method, swap the comparator expression, replacing:
while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 1 : 2*currentIndex + 2) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
with:
while((this.get(greterChild = this.get(2*currentIndex+1) <= this.get(2*currentIndex + 2) ? 2*currentIndex + 2 : 2*currentIndex + 1) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
In the pop method, replace:
this.sink(index);
with:
this.sink(index);
this.bubble(index);
(You can also first check which of both is needed, but it doesn't hurt to just call both methods)

Recursive function to reverse a number

I tried to use a recursive function to reverse a number it works but for one call only it's because of scoping i guess but i don't know how to fix it
let num;
let reversed='';
let result;
function reverseNum(n){
for(let i =0; i<n; i++){
num = n%10; // get the last digit e.g 352 %10 = 2
reversed+= num
result = parseInt(n / 10); // remove last digit e.g. parseInt(352/10) = 35
reverseNum(result);
if(result ===0){
break;
}
}
return reversed;
}
You need the num, reversed, and result variables to be created anew each time the function is called externally. Here's one simple tweak, by defining the recursive function inside the top reverseNum function:
function reverseNum(n) {
let num;
let reversed = '';
let result;
const recurse = (n) => {
for (let i = 0; i < n; i++) {
num = n % 10; // get the last digit e.g 352 %10 = 2
reversed += num
result = parseInt(n / 10); // remove last digit e.g. parseInt(352/10) = 35
recurse(result);
if (result === 0) {
break;
}
}
return reversed;
};
return recurse(n);
}
console.log(reverseNum(1234));
console.log(reverseNum(1234));
But a more elegant method would be:
function reverseNum(n, str = String(n)) {
const thisDigit = str[str.length - 1];
const recursiveResult = str.length === 1 ? '' : reverseNum(str.slice(0, str.length - 1));
return Number(thisDigit + recursiveResult);
}
console.log(reverseNum(1234));
console.log(reverseNum(1234));
function reverse(number){
let index = 0 ;
let reversed = '';
let max = `${number}`.toString().length-1;
while(index <= max ){
reversed += `${number}`.charAt(max-index)
index ++;
}
return reversed;
}
console.log(reverse(546))
CertainPerformance has explained why your code wasn't working.
Here is another implementation, one I find reasonably simple:
const reverseNum = (n) =>
n < 10
? String(n)
: String (n % 10) + reverseNum (Math .floor (n / 10))
console .log (reverseNum (8675309))
Note that this returns a String rather than a Number. If we wanted to, we could make this a private function and make a public function one which called this and converted the result back into a number. But that would have the weird effect that reversing, say, 1000 would yield 1, since 0001 is simplified to 1. And that would mean that when you reverse again, you don't get anything like your original value. So I choose to keep with a String.
Of course if we're going to do String reversal, perhaps we're better off just using a String reversal function in the first place:
const reverseStr = (s) =>
s.length == 0
? ''
: reverseStr (s .slice (1)) + s [0]
const reverseNum = (n) =>
reverseStr (String(n))
console .log (reverseNum (8675309))
Or if we weren't interested in doing this recursively, we could just write the more common string reversal function:
const reverseStr = (s) =>
s .split ('') .reverse () .join ('')
const reverseNum = (n) =>
reverseStr (String (n))
console .log (reverseNum (8675309))

Recursion not properly running?

I'm practicing recursion, and am trying to use it to constantly add individual digits in a number until there is only 1 digit left.
Basically, if the number is 84, it becomes 8+4 = 12 which then becomes 1 + 2 = 3.
Below is my attempt on it. Not sure what I'm missing..
const weirdSum = (num) => {
let result = 0;
const split = num.toString().split('');
if(split.length > 1){
for(let i=0;i<split.length;i++){
result = result + (split[i]*1);
}
weirdSum(result); // pass result as argument, which will be split.
}
return result; // return result if split.length is 1
}
there are 2 mistakes, one you need to return weirdSum(result);
another you are returning result which is 0 you should return num
const weirdSum = (num) => {
let result = 0;
const split = num.toString().split('');
if(split.length > 1){
for(let i=0;i<split.length;i++){
result = result + (split[i]*1);
}
return weirdSum(result); // pass result as argument, which will be split.
}
return num; // return result if split.length is 1
}
console.log(weirdSum(84));
let weirdSum = num => {
const split = num.toString().split('');
if(split.length > 1){
const sum = split.reduce((acc, it) => parseInt(it) + acc, 0)
return weirdSum(sum);
}
return num;
}
console.log(weirdSum(84));
console.log(weirdSum(123456));
const weirdSum = (num) => {
let result = 0;
const split = num.toString().split('');
if(split.length > 1){
for(let i=0;i<split.length;i++){
result = result + (split[i]*1);
}
return weirdSum(result); // pass result as argument, which will be split.
}
return split[0]; // return result if split.length is 1
}
console.log(weirdSum(84))
I just changed your codes to works properly, I have no idea it is optimized or not.
You need ti return the split[0] when recursion stack ends not the result!
This function isn't anonymous but it solves your problem
function weirdSum(num) {
if(parseInt(num/10) == 0) {
return num;
}
var num1 = 0;
while(num != 0) {
var d = parseInt(num%10);
num1=num1+d;
num=parseInt(num/10);
}
return weirdSum(num1);
}
How it works ?
Any single digit number when divided by 10 gives 0 as quotient ( parsing to int is required ), function exists when this condition is met ( the first if ).
In the while loop I'm extracting digits of the number ( starting from the last digit ), when we divide a number by 10, the remainder is always the same as the last digit, then we are adding this to the new num ( num1 ).
In the last step of the while loop, we are shortening the number by removing is last digit by dividing it by 10 and repacing the old num1 by quoatient.
const weirdSum = num => {
// figure out the sum
const sum = [...num + ""].reduce((a, e) => a + (e - 0), 0);
// recurse if necessary
return sum < 10 ? sum : weirdSum(sum);
}
console.log(weirdSum(84));

Conditions for the inner bounds of an array

I have a matrix for which I have a function that picks an element of the array at random using the following code:
npcSelectShip() {
let selectCol = () => {
let colIndex = Math.floor(Math.random() * this.npcSelectionField.length);
let selectCell = () => {
let cellIndex = Math.floor(Math.random() * this.npcSelectionField[colIndex].length);
if (this.npcSelectionField[colIndex][cellIndex].isEmpty === false) {
selectCell();
} else {
this.npcSelectionField[colIndex][cellIndex].isEmpty = false;
this.pickDirection(this.npcSelectionField, colIndex, cellIndex);
}
}
selectCell();
}
selectCol();
}
After this I have another function that searches for the neighbors ( top, right, bottom and left ) of the randomly picked element, randomly picks a neighbor and changes a property:
pickDirection(field, col, cell) {
let neighbors = [];
neighbors.push(
field[col - 1][cell],
field[col + 1][cell],
field[col][cell - 1],
field[col][cell + 1]
);
let randDir = () => {
let randIndex = neighbors[Math.floor(Math.random() * neighbors.length)];
if (randIndex.isEmpty === false) {
randDir();
} else {
randIndex.isEmpty = false;
}
}
randDir();
}
The problem I'm facing is when the the randomly picked element has an index of either 0 or equal to the array lenght, because if it picks a neighbor at index-1 or index+1 its basically "out of bounds" and I get these errors:
TypeError: Cannot read property 'isEmpty' of undefined
TypeError: Cannot read property '9' of undefined
Is there a way I can solve this without having to write a shitload of ifs and elses ?
Appreciate the help.
You could use Array.concat and a default pattern, which returns an empty array.
In combination with concat, empty arrays acts as neutral values.
var neighbors = [].concat(
(field[col - 1] || [])[cell] || [],
(field[col + 1] || [])[cell] || [],
(field[col] || [])[cell - 1] || [],
(field[col] || [])[cell + 1] || []
);
Or use a wrapper for accessing
function getCell(array, col, cell) {
return (array[col] || [])[cell] || [];
}
usage
var neighbors = [].concat(
getCell(field, col - 1, cell),
getCell(field, col + 1, cell),
getCell(field, col, cell - 1),
getCell(field, col, cell + 1)
);
How it works
(field[col - 1] || [])[cell] || []
It tries to get the value of
field[col - 1]
and if the value is undefined,
field[col - 1] || []
it returns an empty array, with the locical logical OR || operator. What we get is either the array of field[col - 1] or an empty array [].
For the next index, we use the same pattern and check if
(field[col - 1] || [])[cell]
exists and if not then we take another empty array as result
(field[col - 1] || [])[cell] || []
Now we have either a truthy value, like an object or an empty array.
This is necessary, because empty arrays are not added to an array with Array.concat.
You have basically 2 options:
First option: Do not pickup in the first/last row/column:
npcSelectShip() {
let selectCol = () => {
let colIndex = Math.floor(Math.random() * (this.npcSelectionField.length-2)) +1;
let selectCell = () => {
let cellIndex = Math.floor(Math.random() * (this.npcSelectionField[colIndex].length-2)) +1;
if (this.npcSelectionField[colIndex][cellIndex].isEmpty === false) {
selectCell();
} else {
this.npcSelectionField[colIndex][cellIndex].isEmpty = false;
this.pickDirection(this.npcSelectionField, colIndex, cellIndex);
}
}
selectCell();
}
selectCol();
}
(see the "-2" and "+1" after Math.random functions call)
Math.floor(Math.random() * (length-2)) +1;
Will take a number between floor(0*(length-2)) +1 = 1 and floor(1*(length-2)) +1 = length-1
Second option: Make condition to not add as a neighbor something out of range
pickDirection(field, col, cell) {
let neighbors = [];
if(col-1 >= 0) {
neighbors.push(field[col - 1][cell]);
}
if(col < field.length) {
neighbors.push(field[col + 1][cell]);
}
if(cell-1 >= 0) {
neighbors.push(field[col][cell -1]);
}
if(cell < field[col].length) {
neighbors.push(field[col][cell +1]);
}
let randDir = () => {
let randIndex = neighbors[Math.floor(Math.random() * neighbors.length)];
if (randIndex.isEmpty === false) {
randDir();
} else {
randIndex.isEmpty = false;
}
}
randDir();
}

How to sort strings in JavaScript numerically

I would like to sort an array of strings (in JavaScript) such that groups of digits within the strings are compared as integers not strings. I am not worried about signed or floating point numbers.
For example, the result should be ["a1b3","a9b2","a10b2","a10b11"] not ["a1b3","a10b11","a10b2","a9b2"]
The easiest way to do this seems to be splitting each string on boundaries around groups of digits. Is there a pattern I can pass to String.split to split on character boundaries without removing any characters?
"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];
Or is there another way to compare strings that does not involve splitting them up, perhaps by padding all groups of digits with leading zeros so they are the same length?
"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"
I am working with arbitrary strings, not strings that have a specific arrangement of digit groups.
I like the /(\d+)/ one liner from Gaby to split the array. How backwards compatible is that?
The solutions that parse the strings once in a way that can be used to rebuild the originals are much more efficient that this compare function. None of the answers handle some strings starting with digits and others not, but that would be easy enough to remedy and was not explicit in the original question.
["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"].sort(function (inA, inB) {
var result = 0;
var a, b, pattern = /(\d+)/;
var as = inA.split(pattern);
var bs = inB.split(pattern);
var index, count = as.length;
if (('' === as[0]) === ('' === bs[0])) {
if (count > bs.length)
count = bs.length;
for (index = 0; index < count && 0 === result; ++index) {
a = as[index]; b = bs[index];
if (index & 1) {
result = a - b;
} else {
result = !(a < b) ? (a > b) ? 1 : 0 : -1;
}
}
if (0 === result)
result = as.length - bs.length;
} else {
result = !(inA < inB) ? (inA > inB) ? 1 : 0 : -1;
}
return result;
}).toString();
Result: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"
Another variant is to use an instance of Intl.Collator with the numeric option:
var array = ["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"];
var collator = new Intl.Collator([], {numeric: true});
array.sort((a, b) => collator.compare(a, b));
console.log(array);
I think this does what you want
function sortArray(arr) {
var tempArr = [], n;
for (var i in arr) {
tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g);
for (var j in tempArr[i]) {
if( ! isNaN(n = parseInt(tempArr[i][j])) ){
tempArr[i][j] = n;
}
}
}
tempArr.sort(function (x, y) {
for (var i in x) {
if (y.length < i || x[i] < y[i]) {
return -1; // x is longer
}
if (x[i] > y[i]) {
return 1;
}
}
return 0;
});
for (var i in tempArr) {
arr[i] = tempArr[i].join('');
}
return arr;
}
alert(
sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",")
);
Assuming you want to just do a numeric sort by the digits in each array entry (ignoring the non-digits), you can use this:
function sortByDigits(array) {
var re = /\D/g;
array.sort(function(a, b) {
return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10));
});
return(array);
}
It uses a custom sort function that removes the digits and converts to a number each time it's asked to do a comparison. You can see it work here: http://jsfiddle.net/jfriend00/t87m2/.
Use this compare function for sorting...
function compareLists(a, b) {
var alist = a.split(/(\d+)/), // Split text on change from anything
// to digit and digit to anything
blist = b.split(/(\d+)/); // Split text on change from anything
// to digit and digit to anything
alist.slice(-1) == '' ? alist.pop() : null; // Remove the last element if empty
blist.slice(-1) == '' ? blist.pop() : null; // Remove the last element if empty
for (var i = 0, len = alist.length; i < len; i++) {
if (alist[i] != blist[i]){ // Find the first non-equal part
if (alist[i].match(/\d/)) // If numeric
{
return +alist[i] - +blist[i]; // Compare as number
} else {
return alist[i].localeCompare(blist[i]); // Compare as string
}
}
}
return true;
}
Syntax
var data = ["a1b3", "a10b11", "b10b2", "a9b2", "a1b20", "a1c4"];
data.sort(compareLists);
alert(data);
There is a demo at http://jsfiddle.net/h9Rqr/7/.
Here's a more complete solution that sorts according to both letters and numbers in the strings
function sort(list) {
var i, l, mi, ml, x;
// copy the original array
list = list.slice(0);
// split the strings, converting numeric (integer) parts to integers
// and leaving letters as strings
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].match(/(\d+|[a-z]+)/g);
for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) {
x = parseInt(list[i][mi], 10);
list[i][mi] = !!x || x === 0 ? x : list[i][mi];
}
}
// sort deeply, without comparing integers as strings
list = list.sort(function(a, b) {
var i = 0, l = a.length, res = 0;
while( res === 0 && i < l) {
if( a[i] !== b[i] ) {
res = a[i] < b[i] ? -1 : 1;
break;
}
// If you want to ignore the letters, and only sort by numbers
// use this instead:
//
// if( typeof a[i] === "number" && a[i] !== b[i] ) {
// res = a[i] < b[i] ? -1 : 1;
// break;
// }
i++;
}
return res;
});
// glue it together again
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].join("");
}
return list;
}
I needed a way to take a mixed string and create a string that could be sorted elsewhere, so that numbers sorted numerically and letters alphabetically. Based on answers above I created the following, which pads out all numbers in a way I can understand, wherever they appear in the string.
function padAllNumbers(strIn) {
// Used to create mixed strings that sort numerically as well as non-numerically
var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries
var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups
var result = "";
for (var i=0;i<astrIn.length; i++) {
if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out
if (isNaN(astrIn[i])) {
result += astrIn[i];
} else {
result += padOneNumberString("000000000",astrIn[i]);
}
}
}
return result;
}
function padOneNumberString(pad,strNum,left) {
// Pad out a string at left (or right)
if (typeof strNum === "undefined") return pad;
if (typeof left === "undefined") left = true;
var padLen = pad.length - (""+ strNum).length;
var padding = pad.substr(0,padLen);
return left? padding + strNum : strNum + padding;
}
Sorting occurs from left to right unless you create a custom algorithm. Letters or digits are compared digits first and then letters.
However, what you want to accomplish as per your own example (a1, a9, a10) won’t ever happen. That would require you knowing the data beforehand and splitting the string in every possible way before applying the sorting.
One final alternative would be:
a) break each and every string from left to right whenever there is a change from letter to digit and vice versa; &
b) then start the sorting on those groups from right-to-left. That will be a very demanding algorithm. Can be done!
Finally, if you are the generator of the original "text", you should consider NORMALIZING the output where a1 a9 a10 could be outputted as a01 a09 a10. This way you could have full control of the final version of the algorithm.

Categories