0

I read a few answers on SO about the problems when dealing with splice (including Move an array element from one array position to another) but I was unable to solve my problem.

I am trying to reorder an array in a particular pattern. Let's assume the pattern is that all the negative numbers should be to the left of the array and all the postive numbers should be to the right of the array. The negative and positive numbers should be separated by zeros if any. It is important that the order be maintained

Sample Input   = [3,0,2,-1,0,-3]
Desired Output = [-1,-3,0,0,3,2]

This is the code I wrote

function reorderArray(inputArray) {
origLength = inputArray.length
var indexZero = -1;
  for (var i = 0; i < origLength; i++) {
    if (inputArray[i] > 0) {
        inputArray.push(inputArray[i]);
        inputArray.splice(i, 1);
    } else if (indexZero == -1 && inputArray[i] == 0) {
        indexZero = i;
    } else if (indexZero != -1 && inputArray[i] < 0) {
        inputArray.splice(indexZero, 0, inputArray.splice(i, 1))
        indexZero++;
    }
  }
alert(inputArray);
}

I run into a problem in the first step itself where I attempt to move all positive numbers at the back of the array because of the re-indexing. I can't start from the last index because that would mean losing the order and I can't use the i-- since it would run infinitely.

Any help with how I could make my code work would be appreciated. Additionally, if there is an alternative that is more efficient than splice, that would be welcome too.

Thanks.

Community
  • 1
  • 1
Sid
  • 234
  • 1
  • 4
  • 14
  • 1
    Not sure I get it, looks like you're describing a standard Array.sort -> **http://jsfiddle.net/adeneo/5ncfb2uu/**, but without the order – adeneo Nov 28 '14 at 22:22
  • 2
    Or you could do this -> **http://jsfiddle.net/adeneo/5ncfb2uu/1/** – adeneo Nov 28 '14 at 22:24
  • 3
    As order is important, you may find it easier to filter into 3 separate arrays of -ves,0s,+ves then put them back together – Rhumborl Nov 28 '14 at 22:25
  • I also think it's better to filter values into 3 different arrays. Than in the end, you still can concat those 3 arrays into 1. – Joel Nov 28 '14 at 22:26
  • Thanks everyone. My first approach was 3 separate arrays but I wasn't sure of the space complexity ramifications. Any help on that? – Sid Nov 28 '14 at 22:31
  • @adeneo: Thank you but that does cause a problem if there are duplicates. – Sid Nov 28 '14 at 22:46

3 Answers3

2
var input = [3,0,2,-1,0,-3];

var copy = input.slice(0); // you need an original array copy since you need to
                           // refer to it to maintain the original order

input.sort(function(a, b) {
    var sign_a = Math.sign(a),
        sign_b = Math.sign(b);

    if (sign_a != sign_b) {
        return sign_a - sign_b;
    }

    return copy.indexOf(a) - copy.indexOf(b);
});

console.log(input);

JSFiddle: http://jsfiddle.net/w2g195fy/2/

So you first compare numbers by sign, then you guarantee sort stability by comparing the positions in the original array. The latter is IMPORTANT since Array.prototype.sort() is not guaranteed to be stable.

Another important note: the complexity (from number of comparisons point of view) of this particular implementation is O(N^2 * logN) which may be not acceptable if you have large original array.

Yet another important note: it will not work in case if your input array has duplicate values.

This is a naive implementation that will work with duplicates:

var input = [1, -1, 2, 0, -2, 3, 0, -3, -4, 4, 0];

var groups = input.reduce(function(result, item) {
    result[Math.sign(item)].push(item);
    return result;
}, {'-1': [], 0: [], 1: []});

var result = groups['-1'].concat(groups[0]).concat(groups[1]);

console.log(result)

And here is the most efficient (from both memory and number of comparisons point of view - it's O(N)) solution I can think of:

var input = [1, -4, 2, 0, -2, 3, 0, -3, -4, 4, 0];

var result = [];

for (var sign = -1, len = input.length; sign <= 1; ++sign) {
    for (var i = 0; i < len; ++i) {
        if (Math.sign(input[i]) == sign) {
            result.push(input[i]);
        }
    }
}

console.log(result);

http://jsfiddle.net/pbmw4dtt/

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • Thank you. This does work but like you pointed out, the complexity is a problem and it won't work if there are duplicates. Do you suppose I could replace the positive numbers with undefined in my code? And then have a second run to get rid of the undefined? – Sid Nov 28 '14 at 22:41
  • @Sid: "the complexity is a problem" --- put some constraints into the question then – zerkms Nov 28 '14 at 22:47
  • Sorry about that. There aren't any constraints to start off and I'm terrible at calculating Complexity so I don't even know the probable complexity that my code would eventually have. But I was trying to get to as optimum a solution as possible. Sorry again. The duplicates bit is still a problem though, correct? – Sid Nov 28 '14 at 22:52
  • @Sid What's the problem with duplicates? How should they be handled? – plalx Nov 28 '14 at 22:57
  • @plalx: In the order they appear. So if it's [-3, -1, 0, 2, -3], it should output [-3, -1, -3, 0, 2] – Sid Nov 28 '14 at 23:00
  • @plalx and zerkms: This is the change I made to my code and it seems to be working. But I'm not sure of the space/time complexity. Is it an overkill? http://jsfiddle.net/jceemyen/ Thanks. – Sid Nov 28 '14 at 23:43
  • @Sid Did you look at my implementation? – plalx Nov 29 '14 at 04:46
  • @Sid: "Is it an overkill?" --- does it fit your performance/complexity/memory constraints? – zerkms Nov 29 '14 at 06:55
  • The thing is, these were the kind of questions that were asked in interviews etc. so they ask for as efficient solutions as possible. In both, Time and Space. – Sid Nov 29 '14 at 18:02
  • @Sid: then you just iterate over your array 3 times and push data to a new array. `O(N)` for both comparison number and memory. I wouldn't do it like you in place, since it's `O(N^2)` – zerkms Nov 29 '14 at 20:26
  • Boy! Your codes show that I clearly have a lot to learn by way of library functions. It took me a while to figure it all out. Thanks a lot though. – Sid Nov 29 '14 at 22:06
1

Another way with of doing it with sort:

[3,0,2,-1,0,-3].map(function () { return arguments; }).sort(function (a, b) {
    var aNum = a[0],
        bNum = b[0],
        bothNegative = aNum < 0 && aNum < 0,
        bothPositive = aNum > 0 && bNum > 0;

    if (bothNegative || bothPositive) return a[1] - b[1];
    return aNum - bNum;

}).map(function (arr) { return arr[0]; });

The initial items order is preserved, but they are now segregated in 3 groups. Negatives, zeros and positives.

plalx
  • 42,889
  • 6
  • 74
  • 90
0

This can also be a solution:

var input = [3,0,2,-1,0,-3].join('+');
var output = input.match(/-\d+/g).concat( input.match(/0/g) ).concat( input.match(/(^|\+)[1-9]\d*/g) ).map(parseFloat);

And Fiddle.

It is slower than sort because of last array map (which can be omitted), but idea may be useful.

skobaljic
  • 9,379
  • 1
  • 25
  • 51