How to reduce consecutive integers in an array to hyphenated range expressions? - javascript

In JavaScript, how can I convert a sequence of numbers in an array to a range of numbers? In other words, I want to express consecutive occurring integers (no gaps) as hyphenated ranges.
[2,3,4,5,10,18,19,20] would become [2-5,10,18-20]
[1,6,7,9,10,12] would become [1,6-7,9-10,12]
[3,5,99] would remain [3,5,99]
[5,6,7,8,9,10,11] would become [5-11]

Here is an algorithm that I made some time ago, originally written for C#, now I ported it to JavaScript:
function getRanges(array) {
var ranges = [], rstart, rend;
for (var i = 0; i < array.length; i++) {
rstart = array[i];
rend = rstart;
while (array[i + 1] - array[i] == 1) {
rend = array[i + 1]; // increment the index if the numbers sequential
i++;
}
ranges.push(rstart == rend ? rstart+'' : rstart + '-' + rend);
}
return ranges;
}
getRanges([2,3,4,5,10,18,19,20]);
// returns ["2-5", "10", "18-20"]
getRanges([1,2,3,5,7,9,10,11,12,14 ]);
// returns ["1-3", "5", "7", "9-12", "14"]
getRanges([1,2,3,4,5,6,7,8,9,10])
// returns ["1-10"]

Just having fun with solution from CMS :
function getRanges (array) {
for (var ranges = [], rend, i = 0; i < array.length;) {
ranges.push ((rend = array[i]) + ((function (rstart) {
while (++rend === array[++i]);
return --rend === rstart;
})(rend) ? '' : '-' + rend));
}
return ranges;
}

Very nice question: here's my attempt:
function ranges(numbers){
var sorted = numbers.sort(function(a,b){return a-b;});
var first = sorted.shift();
return sorted.reduce(function(ranges, num){
if(num - ranges[0][1] <= 1){
ranges[0][1] = num;
} else {
ranges.unshift([num,num]);
}
return ranges;
},[[first,first]]).map(function(ranges){
return ranges[0] === ranges[1] ?
ranges[0].toString() : ranges.join('-');
}).reverse();
}
Demo on JSFiddler

I needed TypeScript code today to solve this very problem -- many years after the OP -- and decided to try a version written in a style more functional than the other answers here. Of course, only the parameter and return type annotations distinguish this code from standard ES6 JavaScript.
function toRanges(values: number[],
separator = '\u2013'): string[] {
return values
.slice()
.sort((p, q) => p - q)
.reduce((acc, cur, idx, src) => {
if ((idx > 0) && ((cur - src[idx - 1]) === 1))
acc[acc.length - 1][1] = cur;
else acc.push([cur]);
return acc;
}, [])
.map(range => range.join(separator));
}
Note that slice is necessary because sort sorts in place and we can't change the original array.

Here's my take on this...
function getRanges(input) {
//setup the return value
var ret = [], ary, first, last;
//copy and sort
var ary = input.concat([]);
ary.sort(function(a,b){
return Number(a) - Number(b);
});
//iterate through the array
for (var i=0; i<ary.length; i++) {
//set the first and last value, to the current iteration
first = last = ary[i];
//while within the range, increment
while (ary[i+1] == last+1) {
last++;
i++;
}
//push the current set into the return value
ret.push(first == last ? first : first + "-" + last);
}
//return the response array.
return ret;
}

Using ES6, a solution is:
function display ( vector ) { // assume vector sorted in increasing order
// display e.g.vector [ 2,4,5,6,9,11,12,13,15 ] as "2;4-6;9;11-13;15"
const l = vector.length - 1; // last valid index of vector array
// map [ 2,4,5,6,9,11,12,13,15 ] into array of strings (quote ommitted)
// --> [ "2;", "4-", "-", "6;", "9;", "11-", "-", "13;", "15;" ]
vector = vector.map ( ( n, i, v ) => // n is current number at index i of vector v
i < l && v [ i + 1 ] - n === 1 ? // next number is adjacent ?
`${ i > 0 && n - v [ i - 1 ] === 1 ? "" : n }-` :
`${ n };`
);
return vector.join ( "" ). // concatenate all strings in vector array
replace ( /-+/g, "-" ). // replace multiple dashes by single dash
slice ( 0, -1 ); // remove trailing ;
}
If you want to add extra spaces for readability, just add extra calls to string.prototype.replace().
If the input vector is not sorted, you can add the following line right after the opening brace of the display() function:
vector.sort ( ( a, b ) => a - b ); // sort vector in place, in increasing order.
Note that this could be improved to avoid testing twice for integer adjacentness (adjacenthood? I'm not a native English speaker;-).
And of course, if you don't want a single string as output, split it with ";".

Rough outline of the process is as follows:
Create an empty array called ranges
For each value in sorted input array
If ranges is empty then insert the item {min: value, max: value}
Else if max of last item in ranges and the current value are consecutive then set max of last item in ranges = value
Else insert the item {min: value, max: value}
Format the ranges array as desired e.g. by combining min and max if same
The following code uses Array.reduce and simplifies the logic by combining step 2.1 and 2.3.
function arrayToRange(array) {
return array
.slice()
.sort(function(a, b) {
return a - b;
})
.reduce(function(ranges, value) {
var lastIndex = ranges.length - 1;
if (lastIndex === -1 || ranges[lastIndex].max !== value - 1) {
ranges.push({ min: value, max: value });
} else {
ranges[lastIndex].max = value;
}
return ranges;
}, [])
.map(function(range) {
return range.min !== range.max ? range.min + "-" + range.max : range.min.toString();
});
}
console.log(arrayToRange([2, 3, 4, 5, 10, 18, 19, 20]));

If you simply want a string that represents a range, then you'd find the mid-point of your sequence, and that becomes your middle value (10 in your example). You'd then grab the first item in the sequence, and the item that immediately preceded your mid-point, and build your first-sequence representation. You'd follow the same procedure to get your last item, and the item that immediately follows your mid-point, and build your last-sequence representation.
// Provide initial sequence
var sequence = [1,2,3,4,5,6,7,8,9,10];
// Find midpoint
var midpoint = Math.ceil(sequence.length/2);
// Build first sequence from midpoint
var firstSequence = sequence[0] + "-" + sequence[midpoint-2];
// Build second sequence from midpoint
var lastSequence = sequence[midpoint] + "-" + sequence[sequence.length-1];
// Place all new in array
var newArray = [firstSequence,midpoint,lastSequence];
alert(newArray.join(",")); // 1-4,5,6-10
Demo Online: http://jsbin.com/uvahi/edit

; For all cells of the array
;if current cell = prev cell + 1 -> range continues
;if current cell != prev cell + 1 -> range ended
int[] x = [2,3,4,5,10,18,19,20]
string output = '['+x[0]
bool range = false; --current range
for (int i = 1; i > x[].length; i++) {
if (x[i+1] = [x]+1) {
range = true;
} else { //not sequential
if range = true
output = output || '-'
else
output = output || ','
output.append(x[i]','||x[i+1])
range = false;
}
}
Something like that.

An adaptation of CMS's javascript solution for Cold Fusion
It does sort the list first so that 1,3,2,4,5,8,9,10 (or similar) properly converts to 1-5,8-10.
<cfscript>
function getRanges(nArr) {
arguments.nArr = listToArray(listSort(arguments.nArr,"numeric"));
var ranges = [];
var rstart = "";
var rend = "";
for (local.i = 1; i <= ArrayLen(arguments.nArr); i++) {
rstart = arguments.nArr[i];
rend = rstart;
while (i < ArrayLen(arguments.nArr) and (val(arguments.nArr[i + 1]) - val(arguments.nArr[i])) == 1) {
rend = val(arguments.nArr[i + 1]); // increment the index if the numbers sequential
i++;
}
ArrayAppend(ranges,rstart == rend ? rstart : rstart & '-' & rend);
}
return arraytolist(ranges);
}
</cfscript>

Tiny ES6 module for you guys. It accepts a function to determine when we must break the sequence (breakDetectorFunc param - default is the simple thing for integer sequence input).
NOTICE: since input is abstract - there's no auto-sorting before processing, so if your sequence isn't sorted - do it prior to calling this module
function defaultIntDetector(a, b){
return Math.abs(b - a) > 1;
}
/**
* #param {Array} valuesArray
* #param {Boolean} [allArraysResult=false] if true - [1,2,3,7] will return [[1,3], [7,7]]. Otherwise [[1.3], 7]
* #param {SequenceToIntervalsBreakDetector} [breakDetectorFunc] must return true if value1 and value2 can't be in one sequence (if we need a gap here)
* #return {Array}
*/
const sequenceToIntervals = function (valuesArray, allArraysResult, breakDetectorFunc) {
if (!breakDetectorFunc){
breakDetectorFunc = defaultIntDetector;
}
if (typeof(allArraysResult) === 'undefined'){
allArraysResult = false;
}
const intervals = [];
let from = 0, to;
if (valuesArray instanceof Array) {
const cnt = valuesArray.length;
for (let i = 0; i < cnt; i++) {
to = i;
if (i < cnt - 1) { // i is not last (to compare to next)
if (breakDetectorFunc(valuesArray[i], valuesArray[i + 1])) {
// break
appendLastResult();
}
}
}
appendLastResult();
} else {
throw new Error("input is not an Array");
}
function appendLastResult(){
if (isFinite(from) && isFinite(to)) {
const vFrom = valuesArray[from];
const vTo = valuesArray[to];
if (from === to) {
intervals.push(
allArraysResult
? [vFrom, vTo] // same values array item
: vFrom // just a value, no array
);
} else if (Math.abs(from - to) === 1) { // sibling items
if (allArraysResult) {
intervals.push([vFrom, vFrom]);
intervals.push([vTo, vTo]);
} else {
intervals.push(vFrom, vTo);
}
} else {
intervals.push([vFrom, vTo]); // true interval
}
from = to + 1;
}
}
return intervals;
};
module.exports = sequenceToIntervals;
/** #callback SequenceToIntervalsBreakDetector
#param value1
#param value2
#return bool
*/
first argument is the input sequence sorted array, second is a boolean flag controlling the output mode: if true - single item (outside the intervals) will be returned as arrays anyway: [1,7],[9,9],[10,10],[12,20], otherwise single items returned as they appear in the input array
for your sample input
[2,3,4,5,10,18,19,20]
it will return:
sequenceToIntervals([2,3,4,5,10,18,19,20], true) // [[2,5], [10,10], [18,20]]
sequenceToIntervals([2,3,4,5,10,18,19,20], false) // [[2,5], 10, [18,20]]
sequenceToIntervals([2,3,4,5,10,18,19,20]) // [[2,5], 10, [18,20]]

Here's a version in Coffeescript
getRanges = (array) ->
ranges = []
rstart
rend
i = 0
while i < array.length
rstart = array[i]
rend = rstart
while array[i + 1] - array[i] is 1
rend = array[i + 1] # increment the index if the numbers sequential
i = i + 1
if rstart == rend
ranges.push rstart + ''
else
ranges.push rstart + '-' + rend
i = i + 1
return ranges

I've written my own method that's dependent on Lo-Dash, but doesn't just give you back an array of ranges, rather, it just returns an array of range groups.
[1,2,3,4,6,8,10] becomes:
[[1,2,3,4],[6,8,10]]
http://jsfiddle.net/mberkom/ufVey/

Related

Find all possible function combinations

I have 3, js functions:
function f1(a,b){
return 0;//returns something
}
function f2(a,b){
return 0;//returns something
}
function f3(a){/*note that this has only 1 input: a*/
return 0;//returns something
}
I need to find all possible combinations of these functions up to two functions in a string that is, it need to return the below strings, in form of array
"f1(f1($,$),$)"//used functions:2:- f1,f1
"f1($,f1($,$))"//used functions:2:- f1,f1
"f1(f2($,$),$)"//used functions:2:- f1,f2
"f1($,f2($,$))"//used functions:2:- f1,f2
"f1(f3($),$)"//used functions:2:- f1,f3
"f1($,f3($))"//used functions:2:- f1,f3
"f1($,$)"//used functions:1:- f1
"f2(f1($,$),$)"//used functions:2:- f2,f1
"f2($,f1($,$))"//used functions:2:- f2,f1
"f2(f2($,$),$)"//used functions:2:- f2,f2
"f2($,f2($,$))"//used functions:2:- f2,f2
"f2(f3($),$)"//used functions:2:- f2,f3
"f2($,f3($))"//used functions:2:- f2,f3
"f2($,$)"//used functions:1:- f2
"f3(f1($,$))"//used functions:2:- f3,f1
"f3(f2($,$))"//used functions:2:- f3,f2
"f3(f3($))"//used functions:2:- f3,f3
"f3($)"//used functions:1:- f3
Notes:
$ represents some value that need not be interfered in this Q.s, so let $ ,be $ for this Q.s
the three js functions may vary to any extent, in my case 7 js functions, with six of them having two inputs ,and one function with one input
up to two functions in a string may vary to any extent, like up to 5 functions...
I have learnt to generate possible combinations ,as shown below
/*
* Generate all possible combinations from a list of characters for a given length
*/
function* charCombinations(chars, minLength, maxLength) {
chars = typeof chars === 'string' ? chars : '';
minLength = parseInt(minLength) || 0;
maxLength = Math.max(parseInt(maxLength) || 0, minLength);
//Generate for each word length
for (i = minLength; i <= maxLength; i++) {
//Generate the first word for the combination length by the repetition of first character.
word = (chars[0] || '').repeat(i);
yield word;
//Generate other possible combinations for the word
//Total combinations will be chars.length raised to power of word.length
//Make iteration for all possible combinations
for (j = 1; j < Math.pow(chars.length, i); j++) {
//Make iteration for all indices of the word
for (k = 0; k < i; k++) {
//check if the current index char need to be flipped to the next char.
if (!(j % Math.pow(chars.length, k))) {
// Flip the current index char to the next.
let charIndex = chars.indexOf(word[k]) + 1;
char = chars[charIndex < chars.length ? charIndex : 0];
word = word.substr(0, k) + char + word.substr(k + char.length);
}
}
//Re-oder not neccesary but it makes the words are yeilded alphabetically on ascending order.
yield word.split('').reverse().join('');
}
}
}
let combinations = charCombinations('abc', 1, 3);
let combination = 0;
var carray = [];
while (typeof combination != "undefined") {
combination = combinations.next().value
carray.push(combination);
}
carray.pop();
console.log(carray);
Its is a modification to this blog,
But I don't know how to implement it with a string that is in a form of function with brackets,
I guess using xml will help as shown in here
I have practice of using xml for data and algorithm handling in js,
But still I don't know how to implement it.,Please help
You can use a recursive generator that takes the following input:
An array of function-call strings with $ placeholders for arguments, so for example: ["f1($,$)", "f2($,$)", "f3($)"]. This means you can control how many arguments a function takes, and how many take just 1 (always 1 in your question), or 2, or even more.
A minimum number of function calls to generate per output (this seems to be always 1 in your question)
A maximum number of function calls to generate per output (like 7)
The algorithm passes the current string to the recursive call which can extend it further until the time comes to yield it. The idea is that every $ is found in the string and can be replaced with more nested function calls.
Snippet:
function* combinations(funcs, minCount, maxCount, result="$", start=0) {
if (maxCount <= 0) return yield result;
for (let f of funcs) {
for (let i = result.indexOf("$", start); i >= 0; i = result.indexOf("$", i + 1)) {
yield* combinations(funcs, minCount - 1, maxCount - 1, result.slice(0, i) + f + result.slice(i + 1), i);
}
}
if (minCount <= 0) yield result;
}
// Example run
for (let s of combinations(["f1($,$)", "f2($,$)", "f3($)"], 1, 2)) {
console.log(s);
}
In case your input is just the function objects (references), then you need some preprocessing so to get their names and the number of parameters they are defined with:
function describe(f) {
return `${f.name}(${Array(f.length).fill("$").join(",")})`;
}
function* combinations(funcs, minCount, maxCount, result="$", start=0) {
if (maxCount <= 0) return yield result;
for (let f of funcs) {
for (let i = result.indexOf("$", start); i >= 0; i = result.indexOf("$", i + 1)) {
yield* combinations(funcs, minCount - 1, maxCount - 1, result.slice(0, i) + f + result.slice(i + 1), i);
}
}
if (minCount <= 0) yield result;
}
// Example run
function f1(a,b){ return 0; }
function f2(a,b){ return 0; }
function f3(a){ return 0; }
for (let s of combinations([f1, f2, f3].map(describe), 1, 2)) {
console.log(s);
}

Math operations from string using Javascript

I am trying to find a simple way to perform a set of javascript math operations without using eval() function. Example: 1+2x3x400+32/2+3 and it must follow the PEMDAS math principle. This is what I have, but it doesn't work exactly it should.
function mdas(equation) {
let operations = ["*", "/", "+", "-"];
for (let outerCount = 0; outerCount < operations.length; outerCount++) {
for (let innerCount = 0; innerCount < equation.length; ) {
if (equation[innerCount] == operations[outerCount]) {
let operationResult = runOperation(equation[innerCount - 1], operations[outerCount], equation[innerCount + 1]);
var leftSideOfEquation = equation.substr(0, equation.indexOf(innerCount - 1));
var rightSideOfEquation = equation.substr(equation.indexOf(innerCount), equation.length);
var rightSideOfEquation = rightSideOfEquation.replace(rightSideOfEquation[0],String(operationResult));
equation = leftSideOfEquation + rightSideOfEquation;
innerCount = 0;
}
else {
innerCount++;
}
}
}
return "Here is it: " + equation; //result of the equation
}
If you don't want to use a complete library like mathjs - and you don't want to tackle creating your own script which would involve: lexical analysis, tokenization, syntax analysis, recursive tree parsing, compiling and output...
the simplest banal suggestion: Function
const calc = s => Function(`return(${s})`)();
console.log( calc("1+2*3*400+32/2+3") ); // 2420
console.log( calc("-3*-2") ); // 6
console.log( calc("-3 * + 1") ); // -3
console.log( calc("-3 + -1") ); // -4
console.log( calc("2 * (3 + 1)") ); // 8
My take at a custom MDAS
Here I created a Regex to retrieve operands and operators, accounting for negative values: /(-?[\d.]+)([*\/+-])?/g.
Firstly we need to remove any whitespace from our string using str.replace(/ /g , "")
Using JavaScript's String.prototype.matchAll() we can get a 2D array with all the matches as [[fullMatch, operand, operator], [.. ] we can than further flatten it using Array.prototype.flat()
Having that flattened array, we can now filter it using Array.prototype.filter() to remove the fullMatch -es returned by the regular expression and remove the last undefined value.
Define a calc Object with the needed operation functions
Iterate over the MDAS groups */ and than +- as regular expressions /\/*/ and /+-/
Consume finally the array of matches until only one array key is left
let str = "-1+2 * 3*+400+-32 /2+3.1"; // 2386.1
str = str.replace(/ +/g, ""); // Remove all spaces!
// Get operands and operators as array.
// Remove full matches and undefined values.
const m = [...str.matchAll(/(-?[\d.]+)([*\/+-])?/g)].flat().filter((x, i) => x && i % 3);
const calc = {
"*": (a, b) => a * b,
"/": (a, b) => a / b,
"+": (a, b) => a + b,
"-": (a, b) => a - b,
};
// Iterate by MDAS groups order (first */ and than +-)
[/[*\/]/, /[+-]/].forEach(expr => {
for (let i = 0; i < m.length; i += 2) {
let [a, x, b] = [m[i], m[i + 1], m[i + 2]];
x = expr.exec(x);
if (!x) continue;
m[i] = calc[x.input](parseFloat(a), parseFloat(b)); // calculate and insert
m.splice(i + 1, 2); // remove operator and operand
i -= 2; // rewind loop
}
});
// Get the last standing result
console.log(m[0]); // 2386.1
It's a little hacky, but you can try something like this:
var eqs = [
'1+2*3*4+1+1+3',
'1+2*3*400+32/2+3',
'-5+2',
'3*-2',
];
for(var eq in eqs) { console.log(mdas(eqs[eq])); }
function mdas(equation) {
console.log(equation);
var failsafe = 100;
var num = '(((?<=[*+-])-|^-)?[0-9.]+)';
var reg = new RegExp(num + '([*/])' + num);
while(m = reg.exec(equation)) {
var n = (m[3] == "*") ? m[1]*m[4] : m[1]/m[4];
equation = equation.replace(m[0], n);
if(failsafe--<0) { return 'failsafe'; }
}
var reg = new RegExp(num + '([+-])' + num);
while(m = reg.exec(equation)) {
var n = (m[3] == "+") ? 1*m[1] + 1*m[4] : m[1]-m[4];
equation = equation.replace(m[0], n);
if(failsafe--<0) { return 'failsafe'; }
}
return equation;
}

Compare two sentences word by word and return the number of word matches considering different word forms

Thanks to Nina I have a code to compare two sentences word by word and return the number of word matches like this:
function includeWords(wanted, seen) {
var wantedMap = wanted.split(/\s+/).reduce((m, s) => m.set(s, (m.get(s) || 0) + 1), new Map),
wantedArray = Array.from(wantedMap.keys()),
count = 0;
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => s === t || s.length > 3 && t.length > 3 && (s.startsWith(t) || t.startsWith(s)));
if (!wantedMap.get(key)) return;
console.log(s, key)
++count;
wantedMap.set(key, wantedMap.get(key) - 1);
});
return count;
}
let matches = includeWords('i was sent to earth to protect you introduced', 'they\'re were protecting him i knew that i was aware introducing');
console.log('Matched words: ' + matches);
The code works fine, but there is still one issue:
What if we want to return a match for introduced and introducing too?
If you want the program to consider the words 'introduce' and 'introducing' as a match, it would amount to a "fuzzy" match (non binary logic). One simple way of doing this would require more code, the algorithm of which would possibly resemble
Take 2 words that you wish to match, tokenize into ordered list
of letters
Compare positionally the respective letters, i.e
match a[0]==b[0]? a[1]==b[1] where a[0] represents the first letter
of the first word and b[0] represents the first tokenized
letter/character potential match candidate
KEep a rolling numeric count of such positional matches. In this case it is 8 (introduc).
divide by word length of a = 8/9 call this f
divide by word length of b = 8/11 call this g
Provide a threshold value beyond which the program will consider it a match. eg. if you say anything above 70% in BOTH f and g can be
considered a match - viola, you have your answer!
Please note that there is some normalization also needed to prevent low length words from becoming false positives. you can add a constraint that the aforementioned calculation applies to words with at least 5 letters(or something to that effect!
Hope this helps!!
Regards,
SR
You could calculate similarites for a word pair and get a relation how many characters are similar bei respecting the length of the given word and the wanted pattern.
function getSimilarity(a, b) {
var i = 0;
while (i < a.length) {
if (a[i] !== b[i]) break;
i++;
}
return i / Math.max(a.length, b.length);
}
console.log(getSimilarity('abcdefghij', 'abc')); // 0.3
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdefghij')); // 1
console.log(getSimilarity('abcdef', 'abcdefghij')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'xyz')); // 0
console.log(getSimilarity('introduced', 'introducing')); // 0.7272727272727273
Here's a quick fix solution.
It's not intended as a complete solution.
Since the English language has more than a few quirks that would almost require an AI to understand the language.
First add a function that can compare 2 words and returns a boolean.
It'll also make it easier to test for specific words, and adapt to what's really needed.
For example, here's a function that does the simple checks that were already used.
Plus an '...ed' versus '...ing' check.
function compareWords (word1, word2) {
if (word1 === word2)
return true;
if (word1.length > 3 && word2.length > 3) {
if (word1.startsWith(word2) || word2.startsWith(word1))
return true;
if (word1.length > 4 && word2.length > 4) {
if (/(ing|ed)$/.test(word1) && word1.replace(/(ing|ed)$/, 'inged') === word2.replace(/(ing|ed)$/, 'inged'))
return true;
}
}
return false;
}
//
// tests
//
let words = [
["same", "same"],
["different", "unsame"],
["priced", "pricing"],
["price", "priced"],
["producing", "produced"],
["produced", "producing"]
];
words.forEach( (arr, idx) => {
let word1= arr[0];
let word2= arr[1];
let isSame = compareWords(word1, word2);
console.log(`[${word1}] ≈ [${word2}] : ${isSame}`);
});
Then use it in the code you already have.
...
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => compareWords(t, s));
...
Regarding string similarity, here's f.e. an older SO post that has some methods to compare strings : Compare Strings Javascript Return %of Likely
I have implemented this, it seems to work fine. any suggestions would be appreciated..
let speechResult = "i was sent to earth to introducing protect yourself introduced seen";
let expectSt = ['they were protecting him knew introducing that you i seen was aware seen introducing'];
// Create arrays of words from above sentences
let speechResultWords = speechResult.split(/\s+/);
let expectStWords = expectSt[0].split(/\s+/);
function includeWords(){
// Declare a variable to hold the count number of matches
let arr = [];
for(let a = 0; a < speechResultWords.length; a++){
for(let b = 0; b < expectStWords.length; b++){
if(similarity(speechResultWords[a], expectStWords[b]) > 69){
arr.push(speechResultWords[a]);
console.log(speechResultWords[a] + ' includes in ' + expectStWords[b]);
}
} // End of first for loop
} // End of second for loop
let uniq = [...new Set(arr)];
return uniq.length;
};
let result = includeWords();
console.log(result)
// The algorithmn
function similarity(s1, s2) {
var longer = s1;
var shorter = s2;
if (s1.length < s2.length) {
longer = s2;
shorter = s1;
}
var longerLength = longer.length;
if (longerLength == 0) {
return 1.0;
}
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength)*100;
}
function editDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
var costs = new Array();
for (var i = 0; i <= s1.length; i++) {
var lastValue = i;
for (var j = 0; j <= s2.length; j++) {
if (i == 0)
costs[j] = j;
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue),
costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0)
costs[s2.length] = lastValue;
}
return costs[s2.length];
}

Alternative way to padStart [duplicate]

I am in need of a JavaScript function which can take a value and pad it to a given length (I need spaces, but anything would do). I found this, but I have no idea what the heck it is doing and it doesn't seem to work for me.
String.prototype.pad = function(l, s, t) {
return s || (s = " "),
(l -= this.length) > 0 ?
(s = new Array(Math.ceil(l / s.length) + 1).join(s))
.substr(0, t = !t ? l : t == 1 ?
0 :
Math.ceil(l / 2)) + this + s.substr(0, l - t) :
this;
};
var s = "Jonas";
document.write(
'<h2>S = '.bold(), s, "</h2>",
'S.pad(20, "[]", 0) = '.bold(), s.pad(20, "[]", 0), "<br />",
'S.pad(20, "[====]", 1) = '.bold(), s.pad(20, "[====]", 1), "<br />",
'S.pad(20, "~", 2) = '.bold(), s.pad(20, "~", 2)
);
ECMAScript 2017 (ES8) added String.padStart (along with String.padEnd) for just this purpose:
"Jonas".padStart(10); // Default pad string is a space
"42".padStart(6, "0"); // Pad with "0"
"*".padStart(8, "-/|\\"); // produces '-/|\\-/|*'
If not present in the JavaScript host, String.padStart can be added as a polyfill.
Pre ES8
I found this solution here and this is for me much much simpler:
var n = 123
String("00000" + n).slice(-5); // returns 00123
("00000" + n).slice(-5); // returns 00123
(" " + n).slice(-5); // returns " 123" (with two spaces)
And here I made an extension to the string object:
String.prototype.paddingLeft = function (paddingValue) {
return String(paddingValue + this).slice(-paddingValue.length);
};
An example to use it:
function getFormattedTime(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
hours = hours.toString().paddingLeft("00");
minutes = minutes.toString().paddingLeft("00");
return "{0}:{1}".format(hours, minutes);
};
String.prototype.format = function () {
var args = arguments;
return this.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
This will return a time in the format "15:30".
A faster method
If you are doing this repeatedly, for example to pad values in an array, and performance is a factor, the following approach can give you nearly a 100x advantage in speed (jsPerf) over other solution that are currently discussed on the inter webs. The basic idea is that you are providing the pad function with a fully padded empty string to use as a buffer. The pad function just appends to string to be added to this pre-padded string (one string concat) and then slices or trims the result to the desired length.
function pad(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}
For example, to zero pad a number to a length of 10 digits,
pad('0000000000',123,true);
To pad a string with whitespace, so the entire string is 255 characters,
var padding = Array(256).join(' '), // make a string of 255 spaces
pad(padding,123,true);
Performance Test
See the jsPerf test here.
And this is faster than ES6 string.repeat by 2x as well, as shown by the revised JsPerf here
Please note that jsPerf is no longer online
Please note that the jsPerf site that we originally used to benchmark the various methods is no longer online. Unfortunately, this means we can't get to those test results. Sad but true.
String.prototype.padStart() and String.prototype.padEnd() are currently TC39 candidate proposals: see github.com/tc39/proposal-string-pad-start-end (only available in Firefox as of April 2016; a polyfill is available).
http://www.webtoolkit.info/javascript_pad.html
/**
*
* JavaScript string pad
* http://www.webtoolkit.info/
*
**/
var STR_PAD_LEFT = 1;
var STR_PAD_RIGHT = 2;
var STR_PAD_BOTH = 3;
function pad(str, len, pad, dir) {
if (typeof(len) == "undefined") { var len = 0; }
if (typeof(pad) == "undefined") { var pad = ' '; }
if (typeof(dir) == "undefined") { var dir = STR_PAD_RIGHT; }
if (len + 1 >= str.length) {
switch (dir){
case STR_PAD_LEFT:
str = Array(len + 1 - str.length).join(pad) + str;
break;
case STR_PAD_BOTH:
var padlen = len - str.length;
var right = Math.ceil( padlen / 2 );
var left = padlen - right;
str = Array(left+1).join(pad) + str + Array(right+1).join(pad);
break;
default:
str = str + Array(len + 1 - str.length).join(pad);
break;
} // switch
}
return str;
}
It's a lot more readable.
Here's a recursive approach to it.
function pad(width, string, padding) {
return (width <= string.length) ? string : pad(width, padding + string, padding)
}
An example...
pad(5, 'hi', '0')
=> "000hi"
ECMAScript 2017 adds a padStart method to the String prototype. This method will pad a string with spaces to a given length. This method also takes an optional string that will be used instead of spaces for padding.
'abc'.padStart(10); // " abc"
'abc'.padStart(10, "foo"); // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0"); // "00000abc"
'abc'.padStart(1); // "abc"
A padEnd method was also added that works in the same manner.
For browser compatibility (and a useful polyfill) see this link.
Using the ECMAScript 6 method String#repeat, a pad function is as simple as:
String.prototype.padLeft = function(char, length) {
return char.repeat(Math.max(0, length - this.length)) + this;
}
String#repeat is currently supported in Firefox and Chrome only. for other implementation, one might consider the following simple polyfill:
String.prototype.repeat = String.prototype.repeat || function(n){
return n<=1 ? this : (this + this.repeat(n-1));
}
Using the ECMAScript 6 method String#repeat and Arrow functions, a pad function is as simple as:
var leftPad = (s, c, n) => c.repeat(n - s.length) + s;
leftPad("foo", "0", 5); //returns "00foo"
jsfiddle
edit:
suggestion from the comments:
const leftPad = (s, c, n) => n - s.length > 0 ? c.repeat(n - s.length) + s : s;
this way, it wont throw an error when s.lengthis greater than n
edit2:
suggestion from the comments:
const leftPad = (s, c, n) =>{ s = s.toString(); c = c.toString(); return s.length > n ? s : c.repeat(n - s.length) + s; }
this way, you can use the function for strings and non-strings alike.
The key trick in both those solutions is to create an array instance with a given size (one more than the desired length), and then to immediately call the join() method to make a string. The join() method is passed the padding string (spaces probably). Since the array is empty, the empty cells will be rendered as empty strings during the process of joining the array into one result string, and only the padding will remain. It's a really nice technique.
With ES8, there are two options for padding.
You can check them in the documentation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
Taking up Samuel's ideas, upward here. And remember an old SQL script, I tried with this:
a=1234;
'0000'.slice(a.toString().length)+a;
It works in all the cases I could imagine:
a= 1 result 0001
a= 12 result 0012
a= 123 result 0123
a= 1234 result 1234
a= 12345 result 12345
a= '12' result 0012
Pad with default values
I noticed that I mostly need the padLeft for time conversion / number padding.
So I wrote this function:
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array(b || 2).join(c || 0) + a).slice(-b)
}
This simple function supports Number or String as input.
The default pad is two characters.
The default char is 0.
So I can simply write:
padL(1);
// 01
If I add the second argument (pad width):
padL(1, 3);
// 001
The third parameter (pad character)
padL('zzz', 10, 'x');
// xxxxxxxzzz
#BananaAcid: If you pass a undefined value or a 0 length string, you get 0undefined, so:
As suggested
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array((b || 1) + 1).join(c || 0) + (a || '')).slice(-(b || 2))
}
But this can also be achieved in a shorter way.
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array(b || 2).join(c || 0) + (a || c || 0)).slice(-b)
}
It also works with:
padL(0)
padL(NaN)
padL('')
padL(undefined)
padL(false)
And if you want to be able to pad in both ways:
function pad(a, b, c, d) { // string/number, length=2, char=0, 0/false=Left-1/true=Right
return a = (a || c || 0), c = new Array(b || 2).join(c || 0), d ? (a + c).slice(0, b) : (c + a).slice(-b)
}
which can be written in a shorter way without using slice.
function pad(a, b, c, d) {
return a = (a || c || 0) + '', b = new Array((++b || 3) - a.length).join(c || 0), d ? a+b : b+a
}
/*
Usage:
pad(
input // (int or string) or undefined, NaN, false, empty string
// default:0 or PadCharacter
// Optional
,PadLength // (int) default:2
,PadCharacter // (string or int) default:'0'
,PadDirection // (bolean) default:0 (padLeft) - (true or 1) is padRight
)
*/
Now if you try to pad 'averylongword' with 2... that’s not my problem.
I said that I would give you a tip.
Most of the time, if you pad, you do it for the same value N times.
Using any type of function inside a loop slows down the loop!!!
So if you just want to pad left some numbers inside a long list, don't use functions to do this simple thing.
Use something like this:
var arrayOfNumbers = [1, 2, 3, 4, 5, 6, 7],
paddedArray = [],
len = arrayOfNumbers.length;
while(len--) {
paddedArray[len] = ('0000' + arrayOfNumbers[len]).slice(-4);
}
If you don't know how the maximum padding size based on the numbers inside the array.
var arrayOfNumbers = [1, 2, 3, 4, 5, 6, 7, 49095],
paddedArray = [],
len = arrayOfNumbers.length;
// Search the highest number
var arrayMax = Function.prototype.apply.bind(Math.max, null),
// Get that string length
padSize = (arrayMax(arrayOfNumbers) + '').length,
// Create a Padding string
padStr = new Array(padSize).join(0);
// And after you have all this static values cached start the loop.
while(len--) {
paddedArray[len] = (padStr + arrayOfNumbers[len]).slice(-padSize); // substr(-padSize)
}
console.log(paddedArray);
/*
0: "00001"
1: "00002"
2: "00003"
3: "00004"
4: "00005"
5: "00006"
6: "00007"
7: "49095"
*/
padding string has been inplemented in new javascript version.
str.padStart(targetLength [, padString])
https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/padStart
If you want your own function check this example:
const myString = 'Welcome to my house';
String.prototype.padLeft = function(times = 0, str = ' ') {
return (Array(times).join(str) + this);
}
console.log(myString.padLeft(12, ':'));
//:::::::::::Welcome to my house
Here is a build in method you can use -
str1.padStart(2, '0')
Here's a simple function that I use.
var pad=function(num,field){
var n = '' + num;
var w = n.length;
var l = field.length;
var pad = w < l ? l-w : 0;
return field.substr(0,pad) + n;
};
For example:
pad (20,' '); // 20
pad (321,' '); // 321
pad (12345,' '); //12345
pad ( 15,'00000'); //00015
pad ( 999,'*****'); //**999
pad ('cat','_____'); //__cat
A short way:
(x=>(new Array(int-x.length+1)).join(char)+x)(String)
Example:
(x=>(new Array(6-x.length+1)).join("0")+x)("1234")
return: "001234"
Here is a simple answer in basically one line of code.
var value = 35 // the numerical value
var x = 5 // the minimum length of the string
var padded = ("00000" + value).substr(-x);
Make sure the number of characters in you padding, zeros here, is at least as many as your intended minimum length. So really, to put it into one line, to get a result of "00035" in this case is:
var padded = ("00000" + 35).substr(-5);
ES7 is just drafts and proposals right now, but if you wanted to track compatibility with the specification, your pad functions need:
Multi-character pad support.
Don't truncate the input string
Pad defaults to space
From my polyfill library, but apply your own due diligence for prototype extensions.
// Tests
'hello'.lpad(4) === 'hello'
'hello'.rpad(4) === 'hello'
'hello'.lpad(10) === ' hello'
'hello'.rpad(10) === 'hello '
'hello'.lpad(10, '1234') === '41234hello'
'hello'.rpad(10, '1234') === 'hello12341'
String.prototype.lpad || (String.prototype.lpad = function(length, pad)
{
if(length < this.length)
return this;
pad = pad || ' ';
let str = this;
while(str.length < length)
{
str = pad + str;
}
return str.substr( -length );
});
String.prototype.rpad || (String.prototype.rpad = function(length, pad)
{
if(length < this.length)
return this;
pad = pad || ' ';
let str = this;
while(str.length < length)
{
str += pad;
}
return str.substr(0, length);
});
Array manipulations are really slow compared to simple string concat. Of course, benchmark for your use case.
function(string, length, pad_char, append) {
string = string.toString();
length = parseInt(length) || 1;
pad_char = pad_char || ' ';
while (string.length < length) {
string = append ? string+pad_char : pad_char+string;
}
return string;
};
A variant of #Daniel LaFavers' answer.
var mask = function (background, foreground) {
bg = (new String(background));
fg = (new String(foreground));
bgl = bg.length;
fgl = fg.length;
bgs = bg.substring(0, Math.max(0, bgl - fgl));
fgs = fg.substring(Math.max(0, fgl - bgl));
return bgs + fgs;
};
For example:
mask('00000', 11 ); // '00011'
mask('00011','00' ); // '00000'
mask( 2 , 3 ); // '3'
mask('0' ,'111'); // '1'
mask('fork' ,'***'); // 'f***'
mask('_____','dog'); // '__dog'
If you don't mind including a utility library, lodash library has _.pad, _.padLeft and _.padRight functions.
I think its better to avoid recursion because its costly.
function padLeft(str,size,padwith) {
if(size <= str.length) {
// not padding is required.
return str;
} else {
// 1- take array of size equal to number of padding char + 1. suppose if string is 55 and we want 00055 it means we have 3 padding char so array size should be 3 + 1 (+1 will explain below)
// 2- now join this array with provided padding char (padwith) or default one ('0'). so it will produce '000'
// 3- now append '000' with orginal string (str = 55), will produce 00055
// why +1 in size of array?
// it is a trick, that we are joining an array of empty element with '0' (in our case)
// if we want to join items with '0' then we should have at least 2 items in the array to get joined (array with single item doesn't need to get joined).
// <item>0<item>0<item>0<item> to get 3 zero we need 4 (3+1) items in array
return Array(size-str.length+1).join(padwith||'0')+str
}
}
alert(padLeft("59",5) + "\n" +
padLeft("659",5) + "\n" +
padLeft("5919",5) + "\n" +
padLeft("59879",5) + "\n" +
padLeft("5437899",5));
It's 2014, and I suggest a JavaScript string-padding function. Ha!
Bare-bones: right-pad with spaces
function pad (str, length) {
var padding = (new Array(Math.max(length - str.length + 1, 0))).join(" ");
return str + padding;
}
Fancy: pad with options
/**
* #param {*} str Input string, or any other type (will be converted to string)
* #param {number} length Desired length to pad the string to
* #param {Object} [opts]
* #param {string} [opts.padWith=" "] Character to use for padding
* #param {boolean} [opts.padLeft=false] Whether to pad on the left
* #param {boolean} [opts.collapseEmpty=false] Whether to return an empty string if the input was empty
* #returns {string}
*/
function pad(str, length, opts) {
var padding = (new Array(Math.max(length - (str + "").length + 1, 0))).join(opts && opts.padWith || " "),
collapse = opts && opts.collapseEmpty && !(str + "").length;
return collapse ? "" : opts && opts.padLeft ? padding + str : str + padding;
}
Usage (fancy):
pad("123", 5);
// Returns "123 "
pad(123, 5);
// Returns "123 " - non-string input
pad("123", 5, { padWith: "0", padLeft: true });
// Returns "00123"
pad("", 5);
// Returns " "
pad("", 5, { collapseEmpty: true });
// Returns ""
pad("1234567", 5);
// Returns "1234567"
/**************************************************************************************************
Pad a string to pad_length fillig it with pad_char.
By default the function performs a left pad, unless pad_right is set to true.
If the value of pad_length is negative, less than, or equal to the length of the input string, no padding takes place.
**************************************************************************************************/
if(!String.prototype.pad)
String.prototype.pad = function(pad_char, pad_length, pad_right)
{
var result = this;
if( (typeof pad_char === 'string') && (pad_char.length === 1) && (pad_length > this.length) )
{
var padding = new Array(pad_length - this.length + 1).join(pad_char); //thanks to http://stackoverflow.com/questions/202605/repeat-string-javascript/2433358#2433358
result = (pad_right ? result + padding : padding + result);
}
return result;
}
And then you can do:
alert( "3".pad("0", 3) ); //shows "003"
alert( "hi".pad(" ", 3) ); //shows " hi"
alert( "hi".pad(" ", 3, true) ); //shows "hi "
If you just want a very simple hacky one-liner to pad, just make a string of the desired padding character of the desired max padding length and then substring it to the length of what you want to pad.
Example: padding the string store in e with spaces to 25 characters long.
var e = "hello"; e = e + " ".substring(e.length)
Result: "hello "
If you want to do the same with a number as input just call .toString() on it before.
A friend asked about using a JavaScript function to pad left. It turned into a little bit of an endeavor between some of us in chat to code golf it. This was the result:
function l(p,t,v){
v+="";return v.length>=t?v:l(p,t,p+v);
}
It ensures that the value to be padded is a string, and then if it isn't the length of the total desired length it will pad it once and then recurse. Here is what it looks like with more logical naming and structure
function padLeft(pad, totalLength, value){
value = value.toString();
if( value.length >= totalLength ){
return value;
}else{
return padLeft(pad, totalLength, pad + value);
}
}
The example we were using was to ensure that numbers were padded with 0 to the left to make a max length of 6. Here is an example set:
function l(p,t,v){v+="";return v.length>=t?v:l(p,t,p+v);}
var vals = [6451,123,466750];
var pad = l(0,6,vals[0]);// pad with 0's, max length 6
var pads = vals.map(function(i){ return l(0,6,i) });
document.write(pads.join("<br />"));
A little late, but thought I might share anyway. I found it useful to add a prototype extension to Object. That way I can pad numbers and strings, left or right. I have a module with similar utilities I include in my scripts.
// include the module in your script, there is no need to export
var jsAddOns = require('<path to module>/jsAddOns');
~~~~~~~~~~~~ jsAddOns.js ~~~~~~~~~~~~
/*
* method prototype for any Object to pad it's toString()
* representation with additional characters to the specified length
*
* #param padToLength required int
* entire length of padded string (original + padding)
* #param padChar optional char
* character to use for padding, default is white space
* #param padLeft optional boolean
* if true padding added to left
* if omitted or false, padding added to right
*
* #return padded string or
* original string if length is >= padToLength
*/
Object.prototype.pad = function(padToLength, padChar, padLeft) {
// get the string value
s = this.toString()
// default padToLength to 0
// if omitted, original string is returned
padToLength = padToLength || 0;
// default padChar to empty space
padChar = padChar || ' ';
// ignore padding if string too long
if (s.length >= padToLength) {
return s;
}
// create the pad of appropriate length
var pad = Array(padToLength - s.length).join(padChar);
// add pad to right or left side
if (padLeft) {
return pad + s;
} else {
return s + pad;
}
};
Never insert data somewhere (especially not at beginning, like str = pad + str;), since the data will be reallocated everytime. Append always at end!
Don't pad your string in the loop. Leave it alone and build your pad string first. In the end concatenate it with your main string.
Don't assign padding string each time (like str += pad;). It is much faster to append the padding string to itself and extract first x-chars (the parser can do this efficiently if you extract from first char). This is exponential growth, which means that it wastes some memory temporarily (you should not do this with extremely huge texts).
if (!String.prototype.lpad) {
String.prototype.lpad = function(pad, len) {
while (pad.length < len) {
pad += pad;
}
return pad.substr(0, len-this.length) + this;
}
}
if (!String.prototype.rpad) {
String.prototype.rpad = function(pad, len) {
while (pad.length < len) {
pad += pad;
}
return this + pad.substr(0, len-this.length);
}
}
Here is a JavaScript function that adds a specified number of paddings with a custom symbol. The function takes three parameters.
padMe --> string or number to left pad
pads --> number of pads
padSymble --> custom symbol, default is "0"
function leftPad(padMe, pads, padSymble) {
if(typeof padMe === "undefined") {
padMe = "";
}
if (typeof pads === "undefined") {
pads = 0;
}
if (typeof padSymble === "undefined") {
padSymble = "0";
}
var symble = "";
var result = [];
for(var i=0; i < pads; i++) {
symble += padSymble;
}
var length = symble.length - padMe.toString().length;
result = symble.substring(0, length);
return result.concat(padMe.toString());
}
Here are some results:
> leftPad(1)
"1"
> leftPad(1, 4)
"0001"
> leftPad(1, 4, "0")
"0001"
> leftPad(1, 4, "#")
"###1"
Yet another take at with combination of a couple of solutions:
/**
* pad string on left
* #param {number} number of digits to pad, default is 2
* #param {string} string to use for padding, default is '0' *
* #returns {string} padded string
*/
String.prototype.paddingLeft = function (b, c) {
if (this.length > (b||2))
return this + '';
return (this || c || 0) + '', b = new Array((++b || 3) - this.length).join(c || 0), b + this
};
/**
* pad string on right
* #param {number} number of digits to pad, default is 2
* #param {string} string to use for padding, default is '0' *
* #returns {string} padded string
*/
String.prototype.paddingRight = function (b, c) {
if (this.length > (b||2))
return this + '';
return (this||c||0) + '', b = new Array((++b || 3) - this.length).join(c || 0), this + b
};

Javascript - Sort an array of strings that can contain letters,numbers and dates [duplicate]

var myArray = [
'_aaaa_2013-09-25_ssss9.txt',
'_aaaa_2013-09-25_ssss8.txt',
'_aaaa_2013-09-26_ssss1.txt',
'_aaaa_2013-09-25_ssss10.txt',
'_aaaa_2013-09-26_ssss2.txt',
'_aaaa_2013-09-25_ssss13.txt',
'_aaaa_2013-09-25_ssss5.txt',
'_aaaa_2013-09-25_ssss6.txt',
'_aaaa_2013-09-25_ssss7.txt'
];
I need to sort the array by date and number.
Result should be
var result = [
'_aaaa_2013-09-25_ssss5.txt',
'_aaaa_2013-09-25_ssss6.txt',
'_aaaa_2013-09-25_ssss7.txt',
'_aaaa_2013-09-25_ssss8.txt',
'_aaaa_2013-09-25_ssss9.txt',
'_aaaa_2013-09-25_ssss13.txt',
'_aaaa_2013-09-26_ssss1.txt',
'_aaaa_2013-09-26_ssss2.txt'
];
I have tried below code.this will do the sort by date only but i need to sort by the number which is before '.txt'.How can i do this.
myArray.sort(function (a, b) {
var timeStamp1 = a.substring(a.indexOf('_aaaa') + 6, a.indexOf('_ssss'));
var timeStamp2 = b.substring(b.indexOf('_aaaa') + 6, b.indexOf('_ssss'));
timeStamp1 = new Date(Date.UTC(timeStamp1[0], timeStamp1[1], timeStamp1[2]));
timeStamp2 = new Date(Date.UTC(timeStamp2[0], timeStamp2[1], timeStamp2[2]));
return (timeStamp1 > timeStamp2) ? 1 : (timeStamp2 > timeStamp1 ? -1 : 0);
});
You could do it like this:
var re = /^_aaaa_(\d\d\d\d-\d\d-\d\d)_ssss(\d+)\.txt$/;
var result = myArray.slice().sort( function( a, b ) {
var aa = a.match(re), bb = b.match(re);
return(
aa[1] < bb[1] ? -1 :
aa[1] > bb[1] ? 1 :
aa[2] - bb[2]
);
});
Note the use of .slice() to create a copy of the array. This can be omitted if you want to sort the original array in place. (Thanks to #DerFlatulator for the reminder!)
This worked for me.
myArray.sort(function (a, b) {
var a_s = a.substring(0, a.indexOf('ssss') + 4);
var a_n = a.substring(a.indexOf('ssss') + 4, a.indexOf('.txt'));
var b_s = b.substring(0, b.indexOf('ssss') + 4);
var b_n = b.substring(b.indexOf('ssss') + 4, b.indexOf('.txt'));
if (a_s < b_s)
return -1;
if (a_s > b_s)
return 1;
return parseInt(a_n) - parseInt(b_n);
});
jsFiddle
Sort by numeric value within strings.
This assumes:
numbers are integers
every string is different
dates may be sorted as numbers (year,month,day)
["aa_123","aa_13","aa_2","aa_22_bb_23","aa_22_bb_3"].sort( function ( a , b ) {
var as = a.split(/([0-9]+)/); // splits string retaining separators
var bs = b.split(/([0-9]+)/);
var i,c = Math.min(as.length,bs.length);
for ( i=0;i<c && as[i]===bs[i];++i ) ;
var an = (i&1)?+as[i]:as[i]; // separators (digits) always at odd index
var bn = (i&1)?+bs[i]:bs[i];
return (an<bn)?-1:1; // assumes every string different
} );
result:
[
"aa_2",
"aa_13",
"aa_22_bb_3",
"aa_22_bb_23",
"aa_123"
]
This, extracts the numbers from the given string, and puts a given weight on each numerical part. So you can sort it in any order with given priorities for Count, Day, Month, Year.
function weightedNumSort(myArray,weightNum,weightString) {
var WEIGHTS_NUM = weightNum || [1,2,4,3]; //[YEAR,MONTH,DAY,COUNT], You can pass an array with appropriate weights for the number at the given position in the text, e.g year is the first Number
var WEIGHT_STRING = weightString || 1; //And a weight for the string value. If none get passed, default weights are used
function weightedSum (a,b,i) {
return ( a + b * ( WEIGHTS_NUM [i-1] || 1 ));
}
myArray = myArray.slice().sort(function (a, b) {
var reg = /(\d+)/g //A regex to extract the numerical part
var lNum = a.match(reg) //Extract the numerical parts we now have an array ["2013","09","26","2"]
var rNum = b.match(reg)
var delta = Array.apply(null,{length:lNum.length+1});
delta [0] = 0; //add a 0 at the beginning, for convenience with the reduce function
for (var i=0,j=lNum.length; i < j; i++) {
var value = lNum[i] - rNum[i];
value = ~~ (value / Math.abs (value)) // 1 for positive values, 0 for 0 , -1 for negative values, to make weighting easier
delta[i+1] = value;
}
var weightedNumValue = delta.reduce (weightedSum) //Put a weight on the number parts.
var weightedStrValue = WEIGHT_STRING * ( a > b ? 1 : a < b ? -1 : 0 )
return weightedNumValue + weightedStrValue //Add the weighted values and we have a positive or negative value with a correct weight on the numerical parts
})
return myArray
}
Output
console.log (
weightedNumSort (myArray)
) /*
[
"_aaaa_2013-09-25_ssss5.txt",
"_aaaa_2013-09-25_ssss6.txt",
"_aaaa_2013-09-25_ssss7.txt",
"_aaaa_2013-09-25_ssss8.txt",
"_aaaa_2013-09-25_ssss9.txt",
"_aaaa_2013-09-25_ssss10.txt",
"_aaaa_2013-09-25_ssss13.txt",
"_aaaa_2013-09-26_ssss1.txt",
"_aaaa_2013-09-26_ssss2.txt"
]*/
and a Fiddle

Categories