Splitting function arguments by a null?

Viewed 92

Say my function is called as,

my_function(1, 2, 5, null, 4, null, 1, 3);

and I want to split it into separate segments that last up until a null.

Here's what I have so far:

my_function = (...theArgs) => {
    let _theArgs = [];
    let _tempArray = [];
    let j = 0;
    for (let i = 0; i < theArgs.length; i++) {
        if (theArgs[i] != null) {
            _tempArray[j].push(theArgs[i]); //breaks here
        } else {
            _theArgs[j].push(_tempArray); //and here
            _tempArray = [];
            j++;
        }
    }
    return _theArgs;
}

my_function(1, 2, 5, null, 4, null, 1, 3);

So here I am trying to cycle through each argument passed to the function and split it up into a 2D array. For instance, my_function(1, 2, 5, null, 4, null, 1, 3); would return an array _theArgs where _theArgs[0] = [1, 2, 5], _theArgs[1] = [4] and _theArgs[2] = [1, 3]

I've indicated where my code breaks.. any suggestion to approaching this would be much appreciated

8 Answers

You could search for null and push the parts to the result array.

function fn(...args) {
    var result = [],
        r = 0,
        l;
    while ((l = args.indexOf(null, l)) !== -1) {
        result.push(args.slice(r, l));
        r = l + 1;
        l += 2;
    }
    result.push(args.slice(r));
    return result;
}

console.log(fn(1, 2, 5, null, 4, null, 1, 3));

_tempArray[j].push() fails because _tempArray[j] is not an array. _tempArray is initially an empty array, there's nothing in _tempArray[j], so you can't push onto it. I think you just want _tempArray.push(theArgs[i]).

The same with _theArgs[j].

You also need to push onto _theArgs at the end of the function, to get arguments after the last null.

my_function = (...theArgs) => {
    let _theArgs = [];
    let _tempArray = [];
    for (let i = 0; i < theArgs.length; i++) {
        if (theArgs[i] !== null) {
            _tempArray.push(theArgs[i]);
        } else {
            _theArgs.push(_tempArray);
            _tempArray = [];
        }
    }
    if (_tempArray.length > 0) {
        _theArgs.push(_tempArray);
    }
    return _theArgs;
}

console.log(my_function(1, 2, 5, null, 4, null, 1, 3));

It looks like you're trying to call push() on an undefined nested array object (that is being accessed via the j variable).

Consider making the following changes:

const my_function = (...theArgs) => {
    
    let _theArgs = [];
    let _tempArray = [];
    
    for (let i = 0; i < theArgs.length; i++) {
        if (theArgs[i] != null) {

            // Push to _tempArray directly. No need to access via "j"
            _tempArray.push(theArgs[i]);
        } else {

            // Push to _theArgs directly. No need to access via "j"
            _theArgs.push(_tempArray);
            _tempArray = [];
        }
    }
    
    // Include last args items (if any after final null)
    if(_tempArray.length > 0) {
       _theArgs.push(_tempArray);
    }
    
    return _theArgs;
}

const result = my_function(1, 2, 5, null, 4, null, 1, 3);

console.log(result)

The j index is throwing you off; remove it in both cases (it's an undefined offset; push will create the new element automatically).

Also, you'll need to append the final _tempArray before returning the result to ensure the last row is added.

Here's a cleaner version which avoids indexes entirely:

const myFunction = (...args) => {
  let row = [];
  
  const result = args.reduce((a, e) => {
    if (e === null) {
      a.push(row);
      row = [];
    } 
    else {
      row.push(e);
    }

    return a;
  }, []);
  
  return row.length ? result.concat([row]) : result;
}

console.log(myFunction(1, 2, 5, null, 4, null, 1, 3));

This is what you need: (Since you are pushing you don't need the variable j)

my_function = (...theArgs) => {
    let _theArgs = [];
    let _tempArray = [];
    for (let i = 0; i < theArgs.length; i++) {
        if (theArgs[i] != null) {
            _tempArray.push(theArgs[i]); //breaks here
        } else {
            _theArgs.push(_tempArray); //and here
            _tempArray = [];
        }
    }
    return _theArgs;
}

console.log(my_function(1, 2, 5, null, 4, null, 1, 3));

Short and Sweet - not recommended for server-side scripts because its not as performant as a for loop solution but for the client you may save some bandwidth. Also note that this solution, as presented, only works with numbers, nulls and modern browsers.

my_function = (...theArgs) => {
  var _theArgs = theArgs.toString().split(',,');

  for(arg in _theArgs) {
    _theArgs[arg] = _theArgs[arg].split(',').map(function(v) {return +v })
  }
  return _theArgs;
};

console.log(my_function(1, 2, 5, null, 4, null, 1, 3) );

Another Solution:

function my_function(...args) {
    var i = 0, temp = [], lastElt = null;
    for (j = 0; j < args.length; j++) {
        if (args[j] !== null) {
            if (temp[i] === undefined) {
                temp[i] = [];
            }
            temp[i].push(args[j]);
        } else if (lastElt !== null) {
            i++;
        }
        lastElt = args[j];
    }
    return temp;
}

var test = my_function(null, 1, 2, 5, null, null, 4, null, null, null, 1, 3);
console.log(test);

As covered in earlier answers the problem is caused by processing a second dimension of _theArgs and _tempArray when only one (or the first) dimension should be used.

Removing two occurences of [j] and the j++; line will fix this but needs patch-up code to include an array of arguments that follow the last null argument.

You could use Array.prototype.reduce to avoid explicit loop code and patchups as may be required:

const args2D = (...argList) =>
    argList.reduce(
        (result, arg) => {
            if (arg === null){
                result.push( [] );
            }
            else {
               result[ result.length-1].push( arg);
            }
            return result;
        },
        [[]]
     );

console.log( args2D(1, 2, 5, null, 4, null, 1, 3));

This approach always produces a two dimensional array, with empty inner array(s) if the argument list is empty, or no non-null values follow a null value in the list.

Related