3
\$\begingroup\$

For a code challenge I thought it'd be useful to have a helper function to group positives and negatives into their own sub arrays while maintaining the ordering of the original array. I'm sure there's a better way to do this but wanted to take a crack at it before researching.

This idea came to mind, to track the state of the numbers, either positive or negative, and group them according to state. This is just MVP and I didn't account for 0.

Despite being verbose, does the code make sense> Do you have recommendations for improvement?

(code playground)

//compare two numbers state
function negAndPosCompare(numA: number, numB: number) {
    if (numA > 0 && numB > 0) {
        // we'll call 1 our positive state
        return 1;
    }
    else if (numA < 0 && numB < 0) {
        // we'll call -1 our negative state
        return -1
    }
    else {
        // false is if two numbers are not the same state
        return false;
    }
}

//simply return the state of a single number
function getState (num : number) {
    if (num > 0){
        return 1;
    }
    else if (num < 0) {
        return -1;
    }
}


function subArray_pos_and_neg(arr: number[]) {
    // our final return
    const output: number[][] = [];

    //where we'll hold numbers of the same state, it'll be exclusive, either pos, or neg.
    // we can default it to the very first number. It's a temp because we'll keep recycling it whenever state changes.
    let temp_sameKind = [arr[0]];

    // grab the state right away, are we starting with pos or neg?
    let currentState = getState(arr[0]);

    for (let i = 0; i < arr.length; i++) {
        // check that i is gte -> 1 so we can safely check arr[i-1]
        if (i >= 1) {

            //if the current number compared to the last number are not the same state, then that means the streak of a given state is over
            if (negAndPosCompare(arr[i], arr[i - 1]) === false) {

               //grab the new state.
                currentState = getState(arr[i]);

                //push the numbers of the same state that we've accumulated so far, they belong together and we'll reset our temp holder 
                //to accomodate our new state of numbers
                output.push(temp_sameKind);
                temp_sameKind = [];

                //push the current number right away, it is the first number of our new state. this operation is similiar to how we defaulted our
                // temp_sameKind array right off the bat (before the for loop). if we don't push it now it'll go missing.
                temp_sameKind.push(arr[i]);
            }

            // the current number is of the same state as our current streak, therefore we want to include it in our accumulation. 
            if (negAndPosCompare(arr[i], arr[i - 1]) === currentState) {                
                temp_sameKind.push(arr[i]);
            }
        }
    }
    // this check outside of the for loop is the final push of our accumulated numbers... if we don't accomodate the last state of numbers, 
    // they'll be excluded by our previous logic (since the state doesn't change again in the for loop)
    if (temp_sameKind){
        output.push(temp_sameKind);
    }
    return output; 
}

subArray_pos_and_neg([1,2,-4,2,5,-6,-3,3,-6,8,9]); //?
\$\endgroup\$
0

3 Answers 3

5
\$\begingroup\$

I think your code is great, but it is indeed way too verbose. It makes sense, but I got to admit, it took me some time. To me longer code isn't necessarily easier to understand. For instance your if/else really confused me at first.

Personally, I think you could have improved your code by making much shorter. Here's what I came up with :

function createSubArr(A) {
    let output = [];
    for(let i = 0; i < A.length; i++) {
        let subArr = [A[i]];
        while(A[i] > 0 === A[i + 1] > 0) {
            subArr.push(A[i + 1]);
            i++;
        }
        output.push(subArr);
    }
    return output;
}

console.log(createSubArr([-1, -2, -3, 1, -1, -2, 2, 3, 4, -9, 5, 6, 7]))

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Great, thank you! This short code is really nice, it'll take some going over it for me to get the feel and click of it. As i'm writing the code the verbose way makes more sense but more and more i want to move towards this short style. \$\endgroup\$ Commented Jan 10, 2020 at 21:13
  • 1
    \$\begingroup\$ I stepped through your code with the debugger to help understand it, and i'll rewrite it for the practice... It's really great, just the sort of thing i was looking for, thanks again! I really like how you have a while loop inside your for loop that also can also increment i when appropriate, i hadn't seen or thought of that before, really useful! \$\endgroup\$ Commented Jan 10, 2020 at 21:29
  • \$\begingroup\$ your while needs to check for bounds (i + 1 < A.length) \$\endgroup\$
    – njzk2
    Commented Jan 11, 2020 at 8:12
  • \$\begingroup\$ also, output and subArr should be const as they are never re-assigned \$\endgroup\$
    – njzk2
    Commented Jan 11, 2020 at 8:12
  • \$\begingroup\$ actually this answer would deserve a code review of its own: I would argue that the inner while is not necessary and possibly confusing, as it adds a place where i is incremented, whereas keeping track of the sign of the previous value might be clearer. I find Nate's answer much easier to read \$\endgroup\$
    – njzk2
    Commented Jan 11, 2020 at 8:16
3
\$\begingroup\$

const returnNegatives = arr => arr.filter(n => n < 0)
const returnPositives = arr => arr.filter(n => n >= 0)

const mixedArray = [1, 2, -4, 2, 5, -6, -3, 3, -6, 8, 9, 0]

console.log('The negatives', returnNegatives(mixedArray))
console.log('The positives', returnPositives(mixedArray))

Edit: Think I misunderstood what you wanted with the above, updated version:

const isPositive = n => n >= 0
const mixedArray = [1, 2, -4, 2, 5, -6, -3, 3, -6, 8, 9, 0]

const groupByType = arr => {
  let prev = isPositive(arr[0]), result = [], subArray = []
  arr.forEach(n => {
    if (isPositive(n) === prev) subArray.push(n)
    else {
      result.push(subArray)
      subArray = [n]
    }
    prev = isPositive(n)
  })
  result.push(subArray)
  return result
}

console.log(groupByType(mixedArray))

\$\endgroup\$
2
  • \$\begingroup\$ You failed to catch the flaw: There is a positive 0 and a negative 0, yet your code puts them both in the positives :) \$\endgroup\$ Commented Jan 11, 2020 at 3:11
  • \$\begingroup\$ Nate, yes your second code is what i was asking about, very neat and clean, Thanks! \$\endgroup\$ Commented Jan 11, 2020 at 5:34
3
\$\begingroup\$

You could check the sign of the item and the predecessor and if unequal take a new array as group.

function createSubArr(array) {
    let result = [], group;
    for (let i = 0; i < array.length; i++) {
        if (Math.sign(array[i - 1]) !== Math.sign(array[i])) result.push(group = []);
        group.push(array[i]);
    }
    return result;
}

console.log(createSubArr([-1, -2, -3, 1, -1, -2, 2, 3, 4, -9, 5, 6, 7]))
.as-console-wrapper { max-height: 100% !important; top: 0; }

\$\endgroup\$
2
  • \$\begingroup\$ Great answer, thank you! \$\endgroup\$ Commented Jan 13, 2020 at 23:57
  • \$\begingroup\$ <3 this code, very succinct \$\endgroup\$
    – Nate
    Commented Jan 16, 2020 at 19:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.