add and remove specific array elements using array.splice() - javascript

Is it possible to add to a specific part of an array, and then deleting a specific part of the array, in this case the end value using arr.splice()?
i currently do this like so:
var arr = [1,2,3,4,5,6,7,8,9];
arr.splice(0,0,"test");
which should return:
"test",1,2,3,4,5,6,7,8,9
i then do:
arr.pop();
which should return:
"test",1,2,3,4,5,6,7,8
I was wondering if it's possible to do this via the arr.splice() method or if there is any cleaner method to do the same, as potentially i'll be doing this a few times, so i would end up with something like:
arr.splice(0,0,"test");
arr.pop();
arr.splice(1,0,"test2");
arr.pop();
looking at the array.splice documentation it suggests i can only delete the element in the position i'm putting the new element into, not a different one.

In answer to my original question as confirmed in the comments. It is not possible to provide a separate index to insert and another to remove in an array using the splice method or potentially any other single statement in javascript. The best approach to try to achieve this in a single statement if i really needed it would be to create your own function, which really seems counter productive for the most part.

You can use Array.from() to set the .length and values of specific indexes of an array in a single call
var arr = [1,2,3,4,5,6,7,8,9];
arr = Array.from({length:arr.length - 1, ...arr, 0:"test"});
console.log(arr);
To achieve the pattern described at Question you can alternatively use a loop and Object.assign()
var arr = [1,2,3,4,5,6,7,8,9];
var n = 0;
var prop = "test";
while (arr.length > 1) {
Object.assign(arr, {length:arr.length -1,...arr, [n]:!n ? prop : prop+n});
++n;
console.log(arr);
}

Related

How to update an array after splice in Svelte?

I'm learning Svelte, and read in the documentation that arrays need to be reassigned in order for a component or page to update it. For that they devised a more idiomatic solution. Instead of writing:
messages.push('hello');
messages = messages;
you can write instead:
messages = [...messages, 'hello'];
Alright, makes sense. But then the documentation says:
You can use similar patterns to replace pop, shift, unshift and splice.
But how? I cannot see how you can remove items from an array. More to the point, how could I write the following more idiomatically?
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
You could e.g. use the filter array method to create a new array without the element 'hello':
messages = messages.filter(m => m !== 'hello');
As mentioned, Svelte's reactivity is triggered by assignments. The current Svelte tutorial uses JavaScript's (ES6) spread syntax (three dots) to add the next-higher number to an array, providing a more idiomatic solution than a redundant assignment using push:
function pushNumber() {
numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}
You could use spread syntax to replace pop, shift, unshift and splicethough it might increase the time and complexity of the operation in some cases:
function unshiftNumber() {
numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4
}
function popNumber() {
numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3
}
function shiftNumber() {
numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4
}
function spliceNumber() {
numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)];// 0, 1, 2, 3
}
Spread is just one way to do it, though. The purpose behind not using pop/push etc is to encourage immutability. So any removal can just be a filter, for example.
There are several things to consider here.
Given this code:
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
What's happening here is:
Looking for the first occurrence of the string "hello" in the array
Removing such element from the array, based on the index found.
The assumption here is that "hello" needs to exists, otherwise the could would remove the last item from the array (since indexOf returns -1).
The original array is therefore mutate: depends by the context, that sometimes can be preferable instead of copying the whole array into a new one; otherwise it's generally a better practice avoid such mutation.
So. If you want to have this behavior exactly, probably this is the best code you can have. For example, takes the filter example:
messages = messages.filter(message => message !== "hello")
What's happening here is:
Filter out any element equals to "hello"
Returns a new array without such element
So it's quite different from the original code: first of all, it always loop the whole array. If you have thousands of element, even if you have only one "hello" at the second index, it would always iterate all of them. Maybe it's what you want, maybe not. If the element is unique, such as an id, maybe you want to stop once you find it.
Second, it returns a new array. Again, that usually a better practice than mutate the array, but in some context it's preferable mutate it instead of create a new one.
So, if you want to mutate the original array, it's probably better to stick to your original code.
If, instead, you don't care (such as the example of push), I believe that in the intention of svelte's developers, your code would be roughly translate to:
let i = messages.indexOf("hello");
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];
(Still assuming there is a "hello" message and you're interested only in the first occurrence).
It's unfortunate that JS doesn't have a better syntax to handles slices.
In case you're wandering, filter can also be used to remove elements using a given index:
let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']
You can perform the usual push and pop or `splice on your Array
But because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.
According to All about Immutable Arrays and Objects in JavaScript you can do it this way...
let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];
const indexOfHello = messages.indexOf('hello');
messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];
Note the difference between splice and slice
The splice() method adds/removes items to/from an array, and returns
the removed item(s). Note: This method changes the original array.
Syntax: array.splice(start, deleteCount, itemstoAdd, addThisToo);
But
The slice() method returns the selected elements in an array, as a new array object. The slice() method selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
Note: The original array will not be changed.
In order words
It return a shallow copy of a portion of an array into a new array
object selected from begin to end (end not included). The original
array will not be modified.
Syntax: array.slice(start, end);
You can try this: https://svelte.dev/repl/0dedb37665014ba99e05415a6107bc21?version=3.53.1
use a library called svelox. It allows you to use the Array native api(push/splice...etc.) without reassignment statements.
Spread the spliced array to reassign it to itself ;)
messages = [...messages.splice(messages.indexOf('hello'), 1)];
The goal is to make Svelte detecting that array messages (a property of your component or a variable in the Svelte store) has changed. This is why the array messages must be declared with let or var keyword, not const. This way you're allowed to reassign it. And the reassign operation itself is sufficient to make Svelte detecting that the array has changed.
Perhaps even, simply by doing so works too:
messages = messages.splice(messages.indexOf('hello'), 1);

How to efficiently remove the duplicate valued object from an array?

To simplify my question, suppose the objects are jqlite objects, using the angular.equals function, I can check whether they are equal. My question is: How do we use this function to remove duplicate item from an array of jqLite objects?
Here is what I tried:
// Suppose jqArr is the array stated above:
var result = [];
angular.forEach(jqArr, function(v_i){
if(result.length === 0){
result.push(v_i);
} else {
var isPushed = false;
angular.forEach(result, function(v_j){
if(angualr.equals(v_i, v_j)){
isPushed = true;
}
});
if(isPushed === false){
result.push(v_i);
}
}
})
console.log(result);
Suppose jqArr = [e_1, e_2, e_3, e_1, e_2], where e_i(s) are jQLite elements.
output should be:
[e_1, e_2, e_3]
*Please answer using only javascript and angularJs.
You can use ES6 Set like so:
let arr = [1,1,2,2,2,3,4,5,6,6,6,6,6];
let uniq = [...new Set(arr)];
The uniq array will contain unique values. If the aray is filled with object references, it will naturally work too.
In a more abstract form, you are performing an O(n^2) algorithm (same with indexOf and Set) but you can reduce the complexity to O(nlogn) by adding all the elements to the list without checks, after all the elements have been collected remove the duplicates be sorting (needs only one pass over the array to remove duplicates after sort).
This solution works only if you can store all the duplicates, for a "duplication factor" above 100% this is not efficient.
If you cannot perform a logic sort, hash function can give the same result.

How to manipulate a JavaScript array passed to a function without changing the original argument array?

I would like to work with an array passed to a function while leaving the original array untouched. Here is a simple example of the problem.
function whyDoesArrChange(arr) {
var newArr = arr;
newArr.push(4);
return arr;
}
console.log(whyDoesArrChange([1,2,3]));
// OUT: [1,2,3,4]
I would like only newArr to be changed, but the arr (from the arguments) returns the pushed value as well (returns [1,2,3,4]). How can I avoid this?
When passing an array to a function, the value of that array is a reference, you have to clone it to break the reference.
There are multiple ways to achieve this:
1 - Using .slice();
var newArr = arr.slice();
2 - Using .concat
var newArr = arr.concat([]);
3 - Using JSON.parse & JSON.stringify
var newArr = JSON.parse(JSON.stringify(arr));
You can check more ways and see how they perform in this jsperf I found.
While Marcos' answer is correct, no doubt. There are more pure Array functions that can be used (A pure function is a function that doesn't alter data outside of its' scope).
Usually, if you'd like to do multiple actions on the array, I would go with Marcos' answer and then do those changes as usual. But when that's not the case, the following information may be useful:
Adding: arr.concat([1]);
Subarray (i to j): arr.slice(i, j + 1);
Removing (i to j): arr.slice(0, i).concat(arr.slice(j + 1));
Also, filter and map are pure function that will not alter the array.
In JavaScript, when you use (=) to assign a value from a variable to another one, you're just passing the entire variable, so each time one or another changes, the other will change too.
According to your question, the best way that works for me is using the native .slice() JavaScript method to the arrays object. For your code:
function whyDoesArrChange(arr) {
var newArr = arr.slice();
newArr.push(4);
return arr;
}
Because reference types (arrays and objects) can get modified inside functions when passed as arguments, while primitive types (numbers, strings, booleans etc.) don't.
You can make a shallow copy the array with .slice() and work on that array, or return a new array with the .concat method.
function whyDoesArrChange(arr) {
return arr.concat(4);
}

How to push the array of data to another array through javascript without loop

I have an Json array like data {"alphaNumeric":[]}.
Here I just want to push the another array [mentioned below] of objects to this Data with out loop concept.
data{"numeric":[{"id":"1","alpha":"a"},{"id":"2","alpha":"b"}]}.
I used the below code :
data.alphaNumeric.push(data.numeric);
but the output is :
data{"alphaNumeric":[[{"id":"1","alpha":"a"},{"id":"2","alpha":"b"}]]}.
Expected :
data{"alphaNumeric":[{"id":"1","alpha":"a"},{"id":"2","alpha":"b"}]}.
Help me to resolve.
One solution may be using the concat method. Which isn't really good as it creates a whole new array.
b.alphaNumeric = b.alphaNumeric.concat(a.numeric);
But there is a much better solution using push. It accepts more than just one element, but unfortunately not as an array. This can be however achieved using its apply method:
b.alphaNumeric.push.apply(b.alphaNumeric, a.numeric);
Also you can write your own method (I call it add) which will do this action for you:
Array.prototype.add = function (array) {
this.push.apply(this, array);
return this;
};
b.alphaNumeric.add(a.numeric);
Use concat()
data.alphaNumeric.concat(data.numeric);
.push() and .pop() are for adding and removing single elements in an array. The return value from .concat() is what you're looking for:
var newArr = oldArr.concat(extraArr);

Create nested arrays on a loop in Javascript

When I'm working with data, I normally have the need of create Arrays or Objects on a loop, let's say "on the fly".
Lets say for example I want to rearrange an array grouping the element by one of the array keys: One of the methods I use is to loop trough an for in. But the problem is when I define more than one index on the fly.
for(key in array){
newArray[array[key]['gouping_key']] = array[key];
}
This example works fine. But if you have more than one element with the same grouping_key, this code is going to override your previous element.
So, I try this:
var i = 0;
for(key in array){
newArray[array[key]['gouping_key']][i] = array[key];
i++
}
But when I add that second index the interpreter complains saying that the newArray[array[key]['gouping_key']] is undefined. Problem it doesn´t seems to have on the previous example.
Why is that?
I've made this fiddle with an example in case the previous snippets an explanation would be insuficient and unclear. In the fiddle you have three snippets (two commented out).
The first one is the error I get when trying something like what Iǘe mentioned previously.
The second is the solution I use.
And the third an example of the creation of an array on the fly with only one index.
Summing up, I want to know why, when you add the second index, you get that error.
Thanks!
var i = 0;
for(key in array){
// If nested object doesn't exist, make an empty one.
newArray[array[key]['gouping_key']][i] =
newArray[array[key]['gouping_key']][i] || [];
newArray[array[key]['gouping_key']][i] = array[key];
i++
}
You need to create an array to push to, it's not created for you. You can use the || operator to only create an empty array if it's undefined.
Also, that's a lot of nesting to follow... If I may...
var x, y;
y = 0;
for(key in array){
x = array[key].gouping_key;
newArray[x][y] = newArray[x][y] || []
newArray[x][y] = array[key];
y++
}
See how much more readable that is? It's also faster! You dont have to deeply traverse complex objects over and over again.
First using for in for arrays is no good idea, it does not what you are expecting I think. see: https://stackoverflow.com/a/4261096/1924298. Using a simple for loop (or while) should solve your problem.

Categories