I have an array of following strings:
['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0']
...etc.
I need a solution that will give me following ordered result
['4.5.0', '4.21.0', '4.22.0', '5.1.0', '5.5.1', '6.1.0'].
I tried to implement a sort so it first sorts by the numbers in the first position, than in case of equality, sort by the numbers in the second position (after the first dot), and so on...
I tried using sort() and localeCompare(), but if I have elements '4.5.0' and '4.11.0', I get them sorted as ['4.11.0','4.5.0'], but I need to get ['4.5.0','4.11.0'].
How can I achieve this?
You could prepend all parts to fixed size strings, then sort that, and finally remove the padding again.
var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
arr = arr.map( a => a.split('.').map( n => +n+100000 ).join('.') ).sort()
.map( a => a.split('.').map( n => +n-100000 ).join('.') );
console.log(arr)
Obviously you have to choose the size of the number 100000 wisely: it should have at least one more digit than your largest number part will ever have.
With regular expression
The same manipulation can be achieved without having to split & join, when you use the callback argument to the replace method:
var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
arr = arr.map( a => a.replace(/\d+/g, n => +n+100000 ) ).sort()
.map( a => a.replace(/\d+/g, n => +n-100000 ) );
console.log(arr)
Defining the padding function once only
As both the padding and its reverse functions are so similar, it seemed a nice exercise to use one function f for both, with an extra argument defining the "direction" (1=padding, -1=unpadding). This resulted in this quite obscure, and extreme code. Consider this just for fun, not for real use:
var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
arr = (f=>f(f(arr,1).sort(),-1)) ((arr,v)=>arr.map(a=>a.replace(/\d+/g,n=>+n+v*100000)));
console.log(arr);
Use the sort compare callback function
You could use the compare function argument of sort to achieve the same:
arr.sort( (a, b) => a.replace(/\d+/g, n => +n+100000 )
.localeCompare(b.replace(/\d+/g, n => +n+100000 )) );
But for larger arrays this will lead to slower performance. This is because the sorting algorithm will often need to compare a certain value several times, each time with a different value from the array. This means that the padding will have to be executed multiple times for the same number. For this reason, it will be faster for larger arrays to first apply the padding in the whole array, then use the standard sort, and then remove the padding again.
But for shorter arrays, this approach might still be the fastest. In that case, the so-called natural sort option -- that can be achieved with the extra arguments of localeCompare -- will be more efficient than the padding method:
var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
arr = arr.sort( (a, b) => a.localeCompare(b, undefined, { numeric:true }) );
console.log(arr);
More about the padding and unary plus
To see how the padding works, look at the intermediate result it generates:
[ "100005.100005.100001", "100004.100021.100000", "100004.100022.100000",
"100006.100001.100000", "100005.100001.100000" ]
Concerning the expression +n+100000, note that the first + is the unary plus and is the most efficient way to convert a string-encoded decimal number to its numerical equivalent. The 100000 is added to make the number have a fixed number of digits. Of course, it could just as well be 200000 or 300000. Note that this addition does not change the order the numbers will have when they would be sorted numerically.
The above is just one way to pad a string. See this Q&A for some other alternatives.
If you are looking for a npm package to compare two semver version, https://www.npmjs.com/package/compare-versions is the one.
Then you can sort version like this:
// ES6/TypeScript
import compareVersions from 'compare-versions';
var versions = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
var sorted = versions.sort(compareVersions);
You could split the strings and compare the parts.
function customSort(data, order) {
function isNumber(v) {
return (+v).toString() === v;
}
var sort = {
asc: function (a, b) {
var i = 0,
l = Math.min(a.value.length, b.value.length);
while (i < l && a.value[i] === b.value[i]) {
i++;
}
if (i === l) {
return a.value.length - b.value.length;
}
if (isNumber(a.value[i]) && isNumber(b.value[i])) {
return a.value[i] - b.value[i];
}
return a.value[i].localeCompare(b.value[i]);
},
desc: function (a, b) {
return sort.asc(b, a);
}
}
var mapped = data.map(function (el, i) {
return {
index: i,
value: el.split('')
};
});
mapped.sort(sort[order] || sort.asc);
return mapped.map(function (el) {
return data[el.index];
});
}
var array = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0'];
console.log('sorted array asc', customSort(array));
console.log('sorted array desc ', customSort(array, 'desc'));
console.log('original array ', array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can check in loop if values are different, return difference, else continue
var a=['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
a.sort(function(a,b){
var a1 = a.split('.');
var b1 = b.split('.');
var len = Math.max(a1.length, b1.length);
for(var i = 0; i< len; i++){
var _a = +a1[i] || 0;
var _b = +b1[i] || 0;
if(_a === _b) continue;
else return _a > _b ? 1 : -1
}
return 0;
})
console.log(a)
Though slightly late this would be my solution;
var arr = ["5.1.1","5.1.12","5.1.2","3.7.6","2.11.4","4.8.5","4.8.4","2.10.4"],
sorted = arr.sort((a,b) => {var aa = a.split("."),
ba = b.split(".");
return +aa[0] < +ba[0] ? -1
: aa[0] === ba[0] ? +aa[1] < +ba[1] ? -1
: aa[1] === ba[1] ? +aa[2] < +ba[2] ? -1
: 1
: 1
: 1;
});
console.log(sorted);
Here's a solution I developed based on #trincot's that will sort by semver even if the strings aren't exactly "1.2.3" - they could be i.e. "v1.2.3" or "2.4"
function sortSemVer(arr, reverse = false) {
let semVerArr = arr.map(i => i.replace(/(\d+)/g, m => +m + 100000)).sort(); // +m is just a short way of converting the match to int
if (reverse)
semVerArr = semVerArr.reverse();
return semVerArr.map(i => i.replace(/(\d+)/g, m => +m - 100000))
}
console.log(sortSemVer(["1.0.1", "1.0.9", "1.0.10"]))
console.log(sortSemVer(["v2.1", "v2.0.9", "v2.0.12", "v2.2"], true))
This seems to work provided there are only digits between the dots:
var a = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0']
a = a.map(function (x) {
return x.split('.').map(function (x) {
return parseInt(x)
})
}).sort(function (a, b) {
var i = 0, m = a.length, n = b.length, o, d
o = m < n ? n : m
for (; i < o; ++i) {
d = (a[i] || 0) - (b[i] || 0)
if (d) return d
}
return 0
}).map(function (x) {
return x.join('.')
})
'use strict';
var arr = ['5.1.2', '5.1.1', '5.1.1', '5.1.0', '5.7.2.2'];
Array.prototype.versionSort = function () {
var arr = this;
function isNexVersionBigger (v1, v2) {
var a1 = v1.split('.');
var b2 = v2.split('.');
var len = a1.length > b2.length ? a1.length : b2.length;
for (var k = 0; k < len; k++) {
var a = a1[k] || 0;
var b = b2[k] || 0;
if (a === b) {
continue;
} else
return b < a;
}
}
for (var i = 0; i < arr.length; i++) {
var min_i = i;
for (var j = i + 1; j < arr.length; j++) {
if (isNexVersionBigger(arr[i], arr[j])) {
min_i = j;
}
}
var temp = arr[i];
arr[i] = arr[min_i];
arr[min_i] = temp;
}
return arr;
}
console.log(arr.versionSort());
This solution accounts for version numbers that might not be in the full, 3-part format (for example, if one of the version numbers is just 2 or 2.0 or 0.1, etc).
The custom sort function I wrote is probably mostly what you're looking for, it just needs an array of objects in the format {"major":X, "minor":X, "revision":X}:
var versionArr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
var versionObjectArr = [];
var finalVersionArr = [];
/*
split each version number string by the '.' and separate them in an
object by part (major, minor, & revision). If version number is not
already in full, 3-part format, -1 will represent that part of the
version number that didn't exist. Push the object into an array that
can be sorted.
*/
for(var i = 0; i < versionArr.length; i++){
var splitVersionNum = versionArr[i].split('.');
var versionObj = {};
switch(splitVersionNum.length){
case 1:
versionObj = {
"major":parseInt(splitVersionNum[0]),
"minor":-1,
"revision":-1
};
break;
case 2:
versionObj = {
"major":parseInt(splitVersionNum[0]),
"minor":parseInt(splitVersionNum[1]),
"revision":-1
};
break;
case 3:
versionObj = {
"major":parseInt(splitVersionNum[0]),
"minor":parseInt(splitVersionNum[1]),
"revision":parseInt(splitVersionNum[2])
};
}
versionObjectArr.push(versionObj);
}
//sort objects by parts, going from major to minor to revision number.
versionObjectArr.sort(function(a, b){
if(a.major < b.major) return -1;
else if(a.major > b.major) return 1;
else {
if(a.minor < b.minor) return -1;
else if(a.minor > b.minor) return 1;
else {
if(a.revision < b.revision) return -1;
else if(a.revision > b.revision) return 1;
}
}
});
/*
loops through sorted object array to recombine it's version keys to match the original string's value. If any trailing parts of the version
number are less than 0 (i.e. they didn't exist so we replaced them with
-1) then leave that part of the version number string blank.
*/
for(var i = 0; i < versionObjectArr.length; i++){
var versionStr = "";
for(var key in versionObjectArr[i]){
versionStr = versionObjectArr[i].major;
versionStr += (versionObjectArr[i].minor < 0 ? '' : "." + versionObjectArr[i].minor);
versionStr += (versionObjectArr[i].revision < 0 ? '' : "." + versionObjectArr[i].revision);
}
finalVersionArr.push(versionStr);
}
console.log('Original Array: ',versionArr);
console.log('Expected Output: ',['4.5.0', '4.21.0', '4.22.0', '5.1.0', '5.5.1', '6.1.0']);
console.log('Actual Output: ', finalVersionArr);
Inspired from the accepted answer, but ECMA5-compatible, and with regular string padding (see my comments on the answer):
function sortCallback(a, b) {
function padParts(version) {
return version
.split('.')
.map(function (part) {
return '00000000'.substr(0, 8 - part.length) + part;
})
.join('.');
}
a = padParts(a);
b = padParts(b);
return a.localeCompare(b);
}
Usage:
['1.1', '1.0'].sort(sortCallback);
const arr = ["5.1.1","5.1.12","5.1.2","3.7.6","2.11.4","4.8.5","4.8.4","2.10.4"];
const sorted = arr.sort((a,b) => {
const ba = b.split('.');
const d = a.split('.').map((a1,i)=>a1-ba[i]);
return d[0] ? d[0] : d[1] ? d[1] : d[2]
});
console.log(sorted);
This can be in an easier way using the sort method without hardcoding any numbers and in a more generic way.
enter code here
var arr = ['5.1.2', '5.1.1', '5.1.1', '5.1.0', '5.7.2.2'];
splitArray = arr.map(elements => elements.split('.'))
//now lets sort based on the elements on the corresponding index of each array
//mapped.sort(function(a, b) {
// if (a.value > b.value) {
// return 1;
// }
// if (a.value < b.value) {
// return -1;
// }
// return 0;
//});
//here we compare the first element with the first element of the next version number and that is [5.1.2,5.7.2] 5,5 and 1,7 and 2,2 are compared to identify the smaller version...In the end use the join() to get back the version numbers in the proper format.
sortedArray = splitArray.sort((a, b) => {
for (i in a) {
if (parseInt(a[i]) < parseInt(b[i])) {
return -1;
break
}
if (parseInt(a[i]) > parseInt(b[i])) {
return +1;
break
} else {
continue
}
}
}).map(p => p.join('.'))
sortedArray = ["5.1.0", "5.1.1", "5.1.1", "5.1.2", "5.7.2.2"]
sort 1.0a notation correct
use native localeCompare to sort 1.090 notation
function log(label,val){
document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR"));
}
const sortVersions = (
x,
v = s => s.match(/[a-z]|\d+/g).map(c => c==~~c ? String.fromCharCode(97 + c) : c)
) => x.sort((a, b) => (a + b).match(/[a-z]/)
? v(b) < v(a) ? 1 : -1
: a.localeCompare(b, 0, {numeric: true}))
let v=["1.90.1","1.090","1.0a","1.0.1","1.0.0a","1.0.0b","1.0.0.1","1.0a"];
log(' input : ',v);
log('sorted: ',sortVersions(v));
log('no dups:',[...new Set(sortVersions(v))]);
In ES6 you can go without regex.
const versions = ["0.4", "0.11", "0.4.1", "0.4", "0.4.2", "2.0.1","2", "0.0.1", "0.2.3"];
const splitted = versions.map(version =>
version
.split('.')
.map(i => +i))
.map(i => {
let items;
if (i.length === 1) {
items = [0, 0]
i.push(...items)
}
if (i.length === 2) {
items = [0]
i.push(...items)
}
return i
})
.sort((a, b) => {
for(i in a) {
if (a[i] < b[i]) {
return -1;
}
if (a[i] > b[i]) {
return +1;
}
}
})
.map(item => item.join('.'))
const sorted = [...new Set(splitted)]
If ES6 I do this:
versions.sort((v1, v2) => {
let [, major1, minor1, revision1 = 0] = v1.match(/([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/);
let [, major2, minor2, revision2 = 0] = v2.match(/([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/);
if (major1 != major2) return parseInt(major1) - parseInt(major2);
if (minor1 != minor2) return parseInt(minor1) - parseInt(major2);
return parseInt(revision1) - parseInt(revision2);
});
**Sorted Array Object by dotted version value**
var sampleData = [
{ name: 'Edward', value: '2.1.2' },
{ name: 'Sharpe', value: '2.1.3' },
{ name: 'And', value: '2.2.1' },
{ name: 'The', value: '2.1' },
{ name: 'Magnetic', value: '2.2' },
{ name: 'Zeros', value: '0' },
{ name: 'Zeros', value: '1' }
];
arr = sampleData.map( a => a.value).sort();
var requireData = [];
arr.forEach(function(record, index){
var findRecord = sampleData.find(arr => arr.value === record);
if(findRecord){
requireData.push(findRecord);
}
});
console.log(requireData);
[check on jsfiddle.net][1]
[1]: https://jsfiddle.net/jx3buswq/2/
It is corrected now!!!
function isSorted(set) {
if(set == set.sort()) {
return true;
} else {
return false;
}
}
This function is supposed to check if an array is sorted properly, but no matter what, it still returns true.
From MDN:
The sort() method sorts the elements of an array in place and returns the array.
After set.sort() is invoked, set itself has been sorted, so set will always equal set.sort().
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
As pointed out in the comments, == compares by reference and hence still refers to the same array. Thereby the condition is always true
Use the below method to ensure if the array is sorted or not. Found the method form here
/*
* check the array is sorted
* return: if positive (ascending) -> 1
* if negative (descending) -> -1
* not sorted -> 0
*/
Array.prototype.isSorted = function() {
return (function(direction) {
return this.reduce(function(prev, next, i, arr) {
if (direction === undefined)
return (direction = prev <= next ? 1 : -1) || true;
else
return (direction + 1 ?
(arr[i-1] <= next) :
(arr[i-1] > next));
}) ? Number(direction) : false;
}).call(this);
}
var arr = [3,2,1,0];
arr.isSorted();
Or you can create a similar function like this using the above code
/*
* check the array is sorted
* return: if positive (ascending) -> 1
* if negative (descending) -> -1
* not sorted -> 0
*/
function isSorted(myArray) {
return (function(direction) {
return myArray.reduce(function(prev, next, i, arr) {
if (direction === undefined)
return (direction = prev <= next ? 1 : -1) || true;
else
return (direction + 1 ?
(arr[i-1] <= next) :
(arr[i-1] > next));
}) ? Number(direction) : false;
}).call(myArray);
}
var arr = [3,2,1,0];
isSorted(arr);
I suggest to use another way to check, if an array is sorted. You could use a compareFunction as described by Array#sort and use it as check for test as sorted with Array#every
For example, if you have numbers and a compareFunction like
var greaterOrEqual = function (a, b ) { return a - b; }
you could use the function to sort an array with number like
array.sort(greaterOrEqual);
and you could use the following pattern for checking every element
var isSorted = function (compareFunction) {
return function (a, i, aa) {
return !(compareFunction(aa[i - 1], a) > 0);
};
};
and then use it with an array, like
var isSorted = array.every(isSorted(greaterOrEqual));
A working example
var greaterOrEqual = function (a, b ) { return a - b; },
isSorted = function (compareFunction) {
return function (a, i, aa) {
return !(compareFunction(aa[i - 1], a) > 0);
};
};
console.log([42].every(isSorted(greaterOrEqual))); // true
console.log([42, 42].every(isSorted(greaterOrEqual))); // true
console.log([1, 2, 3, 4, 5].every(isSorted(greaterOrEqual))); // true
console.log([1, 2, 5, 4, 3].every(isSorted(greaterOrEqual))); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }
Straightforward question:
Can I negate a callback function that returns true or false o an array.filter() statement? e.g.
//the callback function
function isEven(element, index, array){
if (index%2 == 0 || index == 0){
return true;
}
return false;
}
//what i want to do with that function
//arr[i] is a string
var evens = arr[i].split("").filter(isEven); //works
var odds = arr[i].split("").filter(!isEven); // doesn't work
the above line gives me the error TypeError: false is not a function
Question with some background:
I'm taking on some Hackerrank challenges and i've come across an exercise that requires to take a string and process it, so the output is: The characters with even index values make a new string and the characters in odd index positions make another string , 0 counts as even.
Input
airplane
Output
evens = 'arln'
odds = 'ipae'
I have already solved it by looping through the string, evaluating the index and then pushing the value to the correspondent new array (which i later convert to a string), but it has occurred to me i could be done in a more functional way, using the Array.prototype.filter() function.
now I create a new function that evaluates whether the index number is even or not, and I'd like to use that same function to fill both arrays (evens and odds), like this (now you can refer to the straightforward question part):
var evens = arr[i].split("").filter(isEven); //works
var odds = arr[i].split("").filter(!isEven); // doesn't work
The simplest way to do this would be to just pass an anonymous function which returns the negated result of isEven.
var evens = arr[i].split("").filter(function(el, index, array) {
return !isEven(el, index, array);
});
But you could take this a step further and write a not function which essentially generates the anonymous function for you. Here's an example of such a function.
var input = [0, 1, 2, 3, 4, 5];
function isEven(value) {
return value % 2 === 0;
}
function not(f) {
return function() {
return !f.apply(null, arguments);
}
}
var output = input.filter(not(isEven));
console.log(output);
If you're in an environment that supports rest parameters then you could write your not function like this.
var input = [0, 1, 2, 3, 4, 5];
function isEven(value) {
return value % 2 === 0;
}
function not(f) {
return function(...args) {
return !f.apply(null, args);
}
}
var output = input.filter(not(isEven));
console.log(output);
You would need to pass in an anonymous function and then negate isEven in there:
var odds = arr[i].split("").filter(function(a, index, array) {
return !isEven(a, index, array);
});
Simple Example:
Working Example
function isEven(n) {
return n % 2 === 0;
}
var arr = [0,1,2,3,4,5,6,7,8,9];
var a = arr.filter(isEven);
var b = arr.filter(function(a) {
return !isEven(a);
});
The solution I use is something like this:
var numbers = [0,1,2,3,4,5];
var evens = [];
var odds = [];
function isEvenOrNot(getEven) {
return function(num) {
if (num % 2 == 0 || num == 0){
return true;
}
return false;
}
}
evens = numbers.filter(isEvenOrNot(true));
odds = numbers.filter(isEvenOrNot(false));
console.log(evens); // [0,2,4]
console.log(odds); // [1,3,5]
I have the following array and sort result:
['53-15-9', '53-15-10', '53-15-8'].sort() // => ["53-15-10", "53-15-8", "53-15-9"]
but I need to get the following result:
["53-15-8", "53-15-9", "53-15-10"];
How could I get desired result?
To compare numbers instead of strings, first remove '-'. When you use arithmetic
operation, JavaScript first coverts it to numbers.
'53-15-9'.replace(/-/g,'') gives '53159'. You can use closures in sort() to pass compare function that can simply subtract a from b. The following function will sort the array ascending:
['53-15-9', '53-15-10', '53-15-8'].sort(function(a,b){
return a.replace(/-/g,'') - b.replace(/-/g,'')
})
Update
As mentioned in the comments, '54-1-1' is less than '53-15-9'. We can change '54-1-1' to '54-01-01'. This only works in double digits. We can do it like:
function customSort(myArray) {
myArray = myArray.map(function(a,b){
var ar = a.split('-');
return ar.map(function(arK, arV){return (arK<10)?'0'+arK : arK.toString();}).join('-');;
})
return myArray.sort(function(a,b){
return a.replace(/-/g,'') - b.replace(/-/g,'')
});
}
customSort(['54-1-2','53-15-9', '53-15-10', '53-15-8']);
// => ["53-15-08", "53-15-09", "53-15-10", "54-01-02"]
You can use a custom function for splitting and sorting the parts.
var array = ['53-15-9', '53-15-10', '53-15-8'];
array.sort(function (a, b) {
var aa = a.split('-'),
bb = b.split('-'),
i, r = 0, l = Math.max(aa.length, bb.length);
for (i = 0; !r && i < l; i++) {
r = (aa[i] || 0) - (bb[i] || 0);
}
return r;
});
document.write('<pre>' + JSON.stringify(array, 0, 4) + '</pre>');
['53-15-9', '53-15-10', '53-15-8'].sort(function(a, b) {
var aNum = parseInt(a.replace(/-/g, ''));
var bNum = parseInt(b.replace(/-/g, ''));
return aNum < bNum ? -1 : aNum > bNum ? 1 : 0;
});
Assuming you want to sort them in numerical order including all the sections, simply remove the -, parse them as an int and then sort the ints in a custom sort function.
var arr = ['53-15-9', '53-15-10', '53-15-8'];
arr.sort(function(a,b){ return a.replace(/-/g,'') - b.replace(/-/g,'') });
console.log(arr);
output
["53-15-8", "53-15-9", "53-15-10"]
Try comparing every number separated by - using Array.prototype.every()
var arr = ["53-15-8", "53-15-9", "53-15-10"];
arr.sort(function(a, b) {
var curr = a.split(/-/).map(Number);
var next = b.split(/-/).map(Number);
return curr.every(function(val, key) {return val <= next[key]}) ? a : b
});
console.log(JSON.stringify(arr))
You need to define your own custom sorting function. Here is an example:
['53-15-9', '53-15-10', '53-15-8','2', '53-14-4','53-15-99'].sort(function(a,b){ // a and b are elements of the array
// split by - and convert values to number
a = a.split('-').map(function(val){return Number(val)})
b = b.split('-').map(function(val){return Number(val)})
// if one has less elements than another, we consider it should go first
if(a.length != b.length) return a.length > b.length
//else, one goes after another if one of its elements is greater than the others at that index
return a.some(function(val, index){
return val > b[index]
}) == 0 ? -1 : 1
})
//output: ["2", "53-14-4", "53-15-8", "53-15-9", "53-15-10", "53-15-99"]
I'm trying to create a function that puts each array element in its own array, recursively.
I think my base case is correct, but my recursive call doesn't appear to be working. any insight?
function ownList(arr){
if (arr.length === 1) {
arr[0] = [arr[0]];
return;
} else {
return arr[0].concat(ownList(arr.slice(1)));
}
}
var arr = [1,2,3]
console.log(ownList(arr))// returns []
//should return [[1],[2],[3]]
Here I'm trying to put each pair in it's own list (recursive only). This code below is correct (update)
function ownListPair(arr){
if (arr.length === 0)
return arr;
else if(arr.length === 1)
return [[arr[0], 0]];
else
return [[arr[0], arr[1]]].concat(ownListPair(arr.slice(2)));
}
// var arr = [3,6,8,1,5]
var arr = [2,7,8,3,1,4]
//returns [ [ 2, 7 ], [ 8, 3 ], [ 1, 4 ]]
console.log(ownListPair(arr))
I prefer this solution for several reasons:
function ownList(a) {
return a.length == 0
? []
: [[a[0]]].concat(ownList(a.slice(1)))
}
It's shorter and more concise
It works for empty arrays as well
The actual wrapping happens only once in the last line. Treating length == 1 separately -- as suggested by others -- is not necessary.
It would more appropriate to make a length of 0 be the null case. Then you just have to get the brackets right. The thing on the left side of the concat should be an array consisting of the array containing the first element.
function ownList(arr) {
return arr.length ? [[arr[0]]].concat(ownList(arr.slice(1))) : [];
}
Here's an alternative, take your pick:
function ownList(arr) {
return arr.length ? [[arr.shift()]] . concat(ownList(arr)) : [];
}
Using a bit of ES6 magic for readability:
function ownList([head, ...tail]) {
return head === undefined ? [] : [[head]] . concat(ownList(tail));
}
Here the [head, ...tail] is using parameter destructuring which pulls the argument apart into its first element (head) and an array of remaining ones (tail).
Instead of concat you could also use the array constructor:
function ownList([head, ...tail]) {
return head === undefined ? [] : Array([head], ...ownList(tail));
}
I think your basic assumption is wrong. What you need to do is check if each item in the array is an array, if not just add the item to the new array, if so have the function run itself on the array item.
That is recursion.
This code does that kind of recursion...
function ownList(arr)
{
var newArr = [];
var length = arr.length;
for (var i = 0; i < length; i++) {
if (typeof(arr[i]) === 'object') {
newArr.push(ownList(arr[i]));
continue;
}
newArr.push([arr[i]]);
}
return newArr;
}
var arr = [1, 2, 3];
console.log(ownList(arr));
Would something like this work:
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]],
result = [];
function flatten(input){
input.forEach(function(el){
if(Array.isArray(el)){
flatten(el)
}else{
result.push([el]);
}
});
}
flatten(arr);
console.log(JSON.stringify(result));
//[[1],[2],[3],["a"],["b"],["c"],["str"]]
JSBIN
Edit:
var result = [];
function flatten(input){
if (input.length === 0){
console.log( "result", result ); //[[1],[2],[3],["a"],["b"],["c"],["str"]]
return;
}
//if zeroth el of input !array, push to result
if (!Array.isArray(input[0])){
result.push(input.splice(0, 1));
flatten(input);
}else{
flatten(input[0]); //else, give input[0] back to flatten
}
}
window.onload = function(){
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]];
flatten(arr);
}
JSBIN
After struggling through this today, turns out that this works :)
function ownList(arr){
//base case:
if (arr.length === 1) {
return [arr];
}
//recurse
//have to do two brackets here --> (arr.slice(0,1)) since length > 1
return [arr.slice(0,1)].concat(ownList(arr.slice(1)));
}
var arr = [1,2,3]
console.log(ownList(arr))// returns [[1],[2],[3]]