4

Not sure if this may be a duplicate, but I am having some troubles trying to think of the best way of shifting an element in an array filled of arrays of elements.

Such as:

var foo = [
    [ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4} ],
    [ {obj: 5}, {obj: 6}, {obj: 7}, {obj: 8} ], 
    [ {obj: 9}, {obj: 10}, {obj: 11}, {obj: 12} ]
];

If I remove one element given an arrayIndex, it would remove that element then shift all of the proceeding elements down to the appropriate array. Such as if I remove obj 3 the result would be:

var arrayIndex = 0;
var objIndex = 2;

var bar = foo[arrayIndex].splice(objIndex, 1);

Result:

bar = [
    [ {obj: 1}, {obj: 2}, {obj: 4}, {obj: 5} ],
    [ {obj: 6}, {obj: 7}, {obj: 8}, {obj: 9} ], 
    [ {obj: 10}, {obj: 11}, {obj: 12} ]
];

Another example would be as shown removing obj 8:

var arrayIndex = 1;
var objIndex = 3;

var bar = foo[arrayIndex].splice(objIndex, 1);

Result:

bar = [
    [ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4} ],
    [ {obj: 5}, {obj: 6}, {obj: 7}, {obj: 9} ], 
    [ {obj: 10}, {obj: 11}, {obj: 12} ]
];

The issue for me is shifting all of the proceeding elements into the correct array position. Additionally, I would like the empty array to be removed. Where foo's length would decrease. foo will also be mutated.

Here was my attempted jsfiddle: https://jsfiddle.net/mLw8kncn/1/

Any help would be appreciated.

8
  • If you remove an element from an array it shifts automatically, the only time that doesn't happen is when you actually use delete. Delete will leave behind an empty space, so the array's length doesn't decrease. If you are using splice, that shouldn't be an issue. Commented Jun 12, 2016 at 6:13
  • I just noticed you want to shift elements into other arrays, I'll try to answer that part. Commented Jun 12, 2016 at 6:22
  • Please show what you've attempted so far. Commented Jun 12, 2016 at 9:50
  • You haven't described what happens to empty arrays either. Commented Jun 12, 2016 at 10:53
  • Sure I will post my attempted one via a plunkr. For an empty array it wouldn't do any deleting and shifting as the element index wouldn't be there. Commented Jun 12, 2016 at 15:48

3 Answers 3

3

A simple way is to store your items in 1D array instead of 2D. Then manipulate the indexes.

var foo = [ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4},
    {obj: 5}, {obj: 6}, {obj: 7}, {obj: 8},
    {obj: 9}, {obj: 10}, {obj: 11}, {obj: 12} ];

function remove(arrayIndex, objIndex) {
    var realIndex = arrayIndex * 4 + objIndex;
    foo.splice(realIndex, 1);
}

Otherwise, you have to rearrange items after every splice.

function remove(arrayIndex, objIndex) {
    foo[arrayIndex].splice(objIndex, 1);

    for (var i = arrayIndex + 1; i < foo.length; i++) {
        var obj = foo[i].shift();
        foo[i - 1].push(obj);
    }

    if (foo[foo.length - 1].length <= 0) {
        foo.pop();
    }
}

And this is much complicated.

Sign up to request clarification or add additional context in comments.

Comments

0

You could use a function that flattens the array temporarily to 1 dimension (keeping record of the original sizes of the sub arrays), then applies the standard splice on that, and finally reconstructs the 2D array based on the recorded size information.

This will have as benefit you can use all the power of splice to delete more than one element at a time and/or insert other elements in the same operation.

The given index must therefore be the index as if the input array were 1-dimensional:

function splice2d(a, start, deleteCount /*, item1, item2, ...*/){
    var flat = [], sizes = [];
    // Make a flat array, keeping record of subarray sizes
    while (a.length) {
        sizes.push(a[0].length);
        flat = flat.concat(a.shift());
    };
    // Apply original splice to flat array
    [].splice.apply(flat, [].slice.call(arguments, 1));
    // Reconstruct 2D array again based on sizes
    while (sizes.length) {
        a.push(flat.splice(0, sizes.shift()));
    }
    return a;
}

// Sample data
var foo = 
    [[{obj: 1}, {obj: 2}, {obj: 3}, {obj: 4}],
    [{obj: 5}, {obj: 6}, {obj: 7}, {obj: 8}], 
    [{obj: 9}, {obj: 10}, {obj: 11}, {obj: 12}]]

// Mutate
splice2d(foo, 2, 1);
// Output result
console.log(foo);

Comments

0

I guess Array.prototype.reduce() is ideal for this job. You may do it like

var        foo =  [[{obj: 1}, {obj: 2}, {obj: 3}, {obj: 4}],
                   [{obj: 5}, {obj: 6}, {obj: 7}, {obj: 8}], 
                   [{obj: 9}, {obj: 10}, {obj: 11}, {obj: 12}]
                  ],
removeAndShift = (a,ai,oi) => ai == a.length-1 ? a[ai].splice(oi,1)
                                               : a.reduce((p,c,i,a) => { if (i == ai+1) {
	                                                                    p.splice(oi,1);
	                                                                    p.push(c.shift());
                                                                         }
                                                                         i > ai+1 && p.push(c.shift());
                                                                         return c;
                                                                       });
 removeAndShift(foo,1,3);
 console.log(foo);

Note that we are not doing anything but simply splicing out the item to delete if the array item to remove an item from is at the very end.

3 Comments

I think you need to check for c.length before pushing and shifting, or you can end up with null being pushed onto an array. Unless the OP wants that of course: the question isn't fully specified.
I wouldn't say reduce is ideal: you're using it purely for side-effects, and the return value doesn't make much sense.
@FizzyTea Agreed with the c.length that might be an issue yet as per reduce.. i have to say that given a couple of neighboring items moving together as previous and current is ideal for this case and i am harnessing this ability of reduce. (p.push(c.shift());) I could have modified the code to return a value exactly as what OP wanted but then it would be much more cryptic. I guess this is cool as it is.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.