16

I have a string that should be executed as an array:

var q = "The, 1, 2, Fox, Jumped, 3, Over, 4";
var z = q.split(',');

If I use split(), it will create an array of strings:

[‘The’, '1', '2', ‘Fox’, ‘Jumped’, '3', ‘Over’, '4'] 

and I don’t need that. I need an array like:

[‘The’, 1, 2, ‘Fox’, ‘Jumped’, 3, ‘Over’, 4]

indicating which is a string and which is a number.

TylerH
  • 20,799
  • 66
  • 75
  • 101
RulerNature
  • 693
  • 4
  • 13
  • 40
  • 3
    A quick note: you should split on "`, `" to get the output you show in your question. – wizzwizz4 Aug 12 '16 at 08:05
  • 3
    What exactly counts as a number? I would think " 1" isn't. How about "0x3fa3" or "35e345" or "fourty-two", or even "čtyřicet dva"? "MCDXIII"? – John Dvorak Aug 12 '16 at 08:11
  • 1
    @wizzwizz4 - I'd even go as far as splitting on `/\s*,\s*` - that way it doesn't matter how many spaces there are and where they are placed. Unless there is absolute certainty of how the string is constructed and that it would never have any other format. – VLAZ Aug 12 '16 at 08:51
  • @Vld Remember the final `/`! – wizzwizz4 Aug 12 '16 at 09:06
  • OK, very relevant here - a lot of the answers are trying (and failing) to do number parsing. http://stackoverflow.com/a/1830844/3689450 here is a very good way backed up by tests for real-world cases. Just use that or something compatible and if it passes, parse it as a number, otherwise don't. – VLAZ Aug 12 '16 at 09:08
  • @wizzwizz4 fudge - yeah, that should be `/\s*,\s*/g`. Also, for completeness' sake, the initial string should also be trimmed to avoid the beginning and final spaces, so `str.trim().split(/\s*,\s*/g)` would separate into words and implicitly trim each. – VLAZ Aug 12 '16 at 09:10
  • 1
    @Vld Now to write an answer. – wizzwizz4 Aug 12 '16 at 09:11

9 Answers9

17

One option is using the Number constructor which returns a number or NaN:

var res = q.split(',').map(el => {
  let n = Number(el);
  return n === 0 ? n : n || el;
});

// > "The, 1, 2, Fox, Jumped, 3.33, Over, -0"
// < ["The", 1, 2, " Fox", " Jumped", 3.33, " Over", -0]

edit: If the above condition is confusing you can replace it with the following condition which was suggested by Bergi:

return isNaN(n) ? el : n;

In case that you want to trim the string elements you can also use the String.prototype.trim method:

return isNaN(n) ? el.trim() : n;
Ram
  • 143,282
  • 16
  • 168
  • 197
  • 5
    That condition is really confusing. Why don't you do explicitly `return isNaN(n) ? el : n;`? – Bergi Aug 12 '16 at 10:01
7
  1. You can use Number function to convert a Stringified number to an actual number.
  2. The Number function would return NaN, if it is not able to convert a string to a Number.

You can use these two facts to our advantage and write something like this

"The, 1, 2, Fox, Jumped, 3, Over, 4".split(/\s*,\s*/g).map(d => Number(d) || d);
// [ 'The', 1, 2, 'Fox', 'Jumped', 3, 'Over', 4 ]

Since, NaN is Falsy (Boolean(NaN) is false), if the Number is not able to convert a string to a number, the actual value will be returned as it is.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
4
var q = "The, 1, 2, Fox, Jumped, 3, Over, 4";
var z = q.split(',');

You could use Array.map, which returns a new array

var newAr = z.map(item => parseInt(item) ? parseInt(item) : item); 

Will output

["The", 1, 2, " Fox", " Jumped", 3, " Over", 4]

kristjan reinhold
  • 2,038
  • 1
  • 17
  • 34
  • 3
    x.split(','); should be q.split(','); – Jorawar Singh Aug 12 '16 at 06:39
  • 3
    And what about '0'? – str Aug 12 '16 at 06:44
  • The `parseInt(item) || 0` produces ' 0' when there is a leading (or trailing) space. – Nico Aug 12 '16 at 06:54
  • 1
    still not correct. You should wrap the `parseInt(item)` with `isNaN(..)` or you end up as 0 as a string when there are leading or training spaces around a 0 – Nico Aug 12 '16 at 07:19
  • You should **never** use `parseInt()` without the 2nd parameter. **Some** browsers **MAY** interpret an input like `0123` as `83` and not the intended `123`. The 2nd parameter should be `10` (decimal base). Also, it doesn't work for any output between `0` and `1`. – Ismael Miguel Aug 12 '16 at 13:43
4

//from the linked SO answer
function isNumeric(n) { 
  return !isNaN(parseFloat(n)) && isFinite(n);
}

var q = "The, 1, 2, Fox, Jumped, 3, Over, 4";
var z = q.trim().split(/\s*,\s*/).map(function(word) {
  return isNumeric(word) ? Number(word) : word;
});

console.log(z);

How does this work?

isNumeric

It's important to note that you need a way of detecting what is and isn't a number. I suggest using the implementation shown here (the same one as my example) as it's robust and would not throw false positives or false negatives. A lot of the answers here will ignore some valid numbers (e.g., +3 or -3) or parse something invalid as a number (e.g., "").

Let's assume you have a function called isNumeric() that returns a boolean for whether an input is numeric or not. If need be, you might need to alter the implementation to suit your needs, e.g., if you only want to use whole number or only positive numbers.

Splitting into separate words

The string you have would need to be separated into separate chunks of words. This is done in the following fashion

var input = " The, 1, 2 , Fox  , Jumped  , 3, Over, 4     ";
input.trim() //make sure there are no beginning and ending spaces
    .split(/\s*,\s*/); //split on a comma surrounded by any amount of spaces. That removes spaces from start/end of each of the words
//["The", "1", "2", "Fox", "Jumped", "3", "Over", "4"]

Using .map

The map() method can be ran on an array to transform it into a new array. This us done by transforming each element using a callback function. The callback given simply checks if a word is actually a number - if so, it parses it as a number using Number for explicit type conversion. Note that you should NOT have new Number(word) as that creates a Number object, not the numeric primitive type.

It might be useful to note that implicit conversion could be done using the + operator. For example:

+"500" //500
+"hello" //NaN

To the in the beginning of my answer could use +word instead of Number(word) but just I find explicit conversion to be easier to understand.

Community
  • 1
  • 1
VLAZ
  • 26,331
  • 9
  • 49
  • 67
3

This can be achieved by iterating over each of the array items (splitting via the , character) and using the map(predict) function. Each of the items can then be tested if they are parseable as an int.

var q = "The, 1, 2, Fox, Jumped, 3, Over, 4";
var z = q.split(',').map(i => !isNaN(parseInt(i)) ? parseInt(i) : i);

Golfed: :)

p=parseInt,q="The, 0 , 2, Fox, Jumped, 3, Over, 4",z=q.split(',').map(i=>!isNaN(p(i))?p(i):i);
Nico
  • 12,493
  • 5
  • 42
  • 62
3

I gonna be late but checked all the answers and none examined all the cases like: 1.3, 11 Balls.. etc. Also you did not trim the extra whitespaces for the words, and it should be included otherwise we can't get desired result as OP requested.

And parseFloat is much better because you don't lose decimals:

var q = "The, 1, 2 fd, Fox, 3.12, Jumped, -3, Over, 4";

var z = q.split(',').map(function(i){
    i = i.trim();
    var reg = /^-?\d+\.?\d*$/;

    if (reg.test(i))
        return parseFloat(i);
    return i;
});

console.log(z);

Result would be:

["The", 1, "2 fd", "Fox", 3.12, "Jumped", -3, "Over", 4]

See the Fiddle

George G
  • 7,443
  • 12
  • 45
  • 59
  • So...is `1e5` not a number, then? What about `+3`? `0xAF`? They look pretty numeric to me. Whether they are _valid_ for OP's case, hasn't been stated by your assumption seems to be wrong for the general case. At the very least, I'd have thought that `+3` should be a valid number. – VLAZ Aug 12 '16 at 08:55
  • this can be easily improved so :p – George G Aug 12 '16 at 08:57
  • Yes it can. I don't think why it should need to - this is a solved problem, doesn't need (faulty) reinventions of the wheel – VLAZ Aug 12 '16 at 09:05
  • I don't understand what are ur arguing to? Also u seem to be irritated, I don't get you. even accepted answer does not implement trimming but u dont say anything. go ur own way child – George G Aug 12 '16 at 10:57
  • I didn't say anything since I realised it was futile to point out _every_ mistake on a problem that shouldn't have been a problem in the first place. Finding numeric values in JavaScript is not what is discussed here - that is a separate thing that, and I repeat _has been solved_. People trying to reinvent it here does nobody any favours - at best, they succeed which accomplishes little to nothing. At worse, they fail and mislead somebody into making a mistake. Either way - the effort is wasted. – VLAZ Aug 12 '16 at 16:00
3

A way which takes advantage of +string conversion to number.

var q = "The, 1, 2, Fox, Jumped, 3, Over, 0";
var r = q.split`, `.map(x=>+x||x==0?+x:x);
console.log(r);
nicael
  • 18,550
  • 13
  • 57
  • 90
  • `+"" //0` - empty strings (resulting from an input like "Hello, , world") would be turned into zeroes. As would spaces ("Hello, , world"). – VLAZ Aug 12 '16 at 09:03
  • Uh, so SO comments remove multiple spaces. The second string should be "Hello,, world". – VLAZ Aug 12 '16 at 09:12
2

I tried loop with forEach and check is number or not by isNaN:return true if not number,return false if number push the new array ...

var q = "The, 1, 2, Fox, Jumped, 3, Over, 4";
var z = [];
q.split(',').forEach(function(v,k){
       isNaN(v) ? z.push(v) : z.push(parseInt(v));
});
console.log(z);
Jack jdeoel
  • 4,554
  • 5
  • 26
  • 52
  • Why not just use `.map`? It does exactly a "`forEach` then push to a new array` only you don't have to create the arrays or anything. Any array transformation is easier to do with `.map()`. Also, why do you even get the key in your callback if you're not going to use it? – VLAZ Aug 12 '16 at 08:55
2

You can use for..of loop, .test() with /\d/ as parameter

var res = []; for (p of q.split(",")) [...res] = [...res, /\d/.test(p) ? +p : p];
guest271314
  • 1
  • 15
  • 104
  • 177