function parsePart(str) { // just a number if(/^-?\d+$/.test(str)) { return parseInt(str, 10); } var m; // 1-5 or 1..5 (equivilant) or 1...5 (doesn't include 5) if((m = str.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/))) { var lhs = m[1]; var sep = m[2]; var rhs = m[3]; if(lhs && rhs) { lhs = parseInt(lhs); rhs = parseInt(rhs); var res = []; var incr = lhs < rhs ? 1 : -1; // Make it inclusive by moving the right 'stop-point' away by one. if(sep == '-' || sep == '..' || sep == '\u2025') { rhs += incr; } for(var i=lhs; i != rhs; i += incr) res.push(i); return res; } } return []; } module.exports.parse = function(str) { var parts = str.split(','); var toFlatten = parts.map(function(el) { return parsePart(el); }); // reduce can't handle single element arrays if(toFlatten.length === 0) return []; if(toFlatten.length === 1) { if(Array.isArray(toFlatten[0])) return toFlatten[0]; return toFlatten; } return toFlatten.reduce(function(lhs, rhs) { if(!Array.isArray(lhs)) lhs = [lhs]; if(!Array.isArray(rhs)) rhs = [rhs]; return lhs.concat(rhs); }); };