sortedIndex for reverse sorted array? - javascript

It seems lodash's sortedIndex expects a forward sorted array for its binary search to work. (e.g. [0,1,2,4])
Is there a way to used sortedIndexBy when the array is reverse sorted? (e.g. [4,2,1,0])?
> _.sortedIndex( [0,1,2,4], 3 )
> 3
> _.sortedIndex( [4,2,1,0], 3 )
> 4
To get this to work now, I have to reverse the array, find the sortedIndex, insert the new element, and then un-reverse the array.
Note -- need something which works for sorting strings as well as numbers.
['A','B','D'] into ['D','B','A'] and insert 'C'.

How about _.sortedIndexBy?
Edited: For string comparison, String.prototype.charCodeAt() can help you convert it to Number, then the same logic can be applied.
const arr1 = [0, 1, 2, 4];
const arr2 = [4, 2 ,1, 0];
console.log(_.sortedIndex(arr1, 3 ));
// Similar, but with ranking function.
console.log(_.sortedIndexBy(arr2, 3, function(x) {return -x;}));
const charArr = ['D','B','A'];
// Take the first char and convert to Number
let index = _.sortedIndexBy(charArr, 'C', function(x) {
// Type checks. (If you want it to be general to many types..
if (typeof x === 'string') {
return -x.charCodeAt(0);
} else if (typeof x === 'number') {
return -x;
} // else ... for other types.....
});
console.log('To insert char C, put it to index: ', index);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
or by _.sortedIndex, it also has iteratee to rank before 4.0.0
const arr1 = [0, 1, 2, 4];
const arr2 = [4, 2 ,1, 0];
console.log(_.sortedIndex(arr1, 3));
console.log("Reversed order without ranking func: ",_.sortedIndex(arr2, 3));
// Ranking function to inverse the order.
console.log("Reversed order with ranking func: ",_.sortedIndex(arr2, 3, function(x) {return -x;}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.3.1/lodash.min.js"></script>
Thanks to pilau:
The sortedIndex expects the array is forward sorted, so we can't just put the reverse sorted array and get arr.length - index, and in order to handle vary scenario, I think we need to do either
Reverse array -> get sorted index and put -> reverse it again. or
Get reversed copy by slice and reverse -> get sorted index and calculate by arr.length - index -> inserted to origin array.
To achieve the expected result.

Related

Sum of two multi dimensional array

I have a array in JavaScript like this.
var arr=
[
['A'],[1,2,3,4],
['A'],[4,3,2,1],
['B'],[10,12,3,1],
['B'],[1,2,3,4],
.
.
.
.
['AZ'],[1,2,3,4]
]
and I want the output to summarize the array like -
var output=
[
['A'],[5,5,5,5],
['B'],[11,14,6,5],
['AZ'],[1,2,3,4]
]
Thanks.
Script
You can use the following script to achieve what you want to do.
const arr = [
["A"],
[1, 2, 3, 4],
["A"],
[4, 3, 2, 1],
["B"],
[10, 12, 3, 1],
["B"],
[1, 2, 3, 4],
["AZ"],
[1, 2, 3, 4],
];
/**
* Generator to return key-value pairs with array[i] being the key and array[i+1] being the value
* #param {Array<any>} array
*/
function* keyValue(array) {
// make sure we can build pairs (other ways of handling this are also possible)
if (array.length % 2 !== 0)
throw new RangeError(
"Array length must be dividable by 2 without remainder!"
);
for (let i = 0; i < array.length; i += 2) {
yield [array[i], array[i + 1]];
}
}
// here the created key-value pairs
console.log("Key-value pairs created by keyValue() generator function:");
console.log([...keyValue(arr)]);
// loop over key value pairs and sum up all the individul arrays based on the letter assigned to them
const result = [...keyValue(arr)].reduce((all, [[key], array]) => {
// if we don't have values for this letter, assing copy of the array to that letter
if (!all[key]) all[key] = [...array];
// we have some values for that letter already, sum up each value
else all[key] = all[key].map((prev, idx) => prev + array[idx]);
return all;
}, {});
// this would be a "better" result to my mind as there is no point wrapping single string values in arrays
// When using objects the values can easily be accessed in O(1)
console.log(result);
// now transform JS object to array of arrays
console.log("Result:");
const transformed = Object.entries(result).flatMap(([key, value]) => [[key], value]);
console.log(transformed);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Please note: This implementation assumes that the arrays for a given letter have the same length (as is the case in your example).
Explanation
First of all, I use a generator function keyValue() to always group two consecutive values in the array (a key and a value) together. One could also do this differently but once you understand how generators work that's an easy and elegant approach, I think. For this demo I just throw an error if the array is not dividable by 2 without remainder, but one could also handle this more gracefully.
Then, using reduce(), I iterate over the array created by using keyValue() and for each element in the array I check if I've encountered that value before. If I have not, I create a copy of the array (for immutablility) and assign it to the key i.e. a letter. If I have encountered a certain letter before I add up the values that I have previously saved assigned to that letter with the ones I am currently processing. After iteration all sums are calculated and I have a JavaScript object containing the results.
To my mind, this would be a good output because your output is a bit odd, since there is no point storing single letters in an array or even arrays of arrays. Using a JavaScript object is much more convenient and faster for lookups.
Nevertheless, you can easily deduct your result from the created object using flatMap().

sorting an array and comparing it to the original

I'm trying to fully understand functional programming when sorting an array why does the original array change to the sorted array also? I
want to check if the array is in ascending order.
let arr = [1,2,8,3,9,88,67];
let newArr = arr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
I want to do something like....
if (arr === newArr) {
return true;
} else {
return false;
}
The original arr is also being sorted so it always is true,
some guidance would be great thanks.
That's just the way the sort operator was designed. It modifies the existing array, rather than creating a new array. If you want to create a new array, you can do it like this:
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice(); // creates a copy of the array
newArr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
Javascript array is an object, When 2 variables reference the same object, changing one would change the value of the other
let obj1 = {x:1, y:2};
let obj2 = obj1;
obj2.x = 3;
console.log(obj1);
You can sort it using
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice().sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
By assigning an object, you take the reference of the object, that means ecery change affects the same object.
For arrays, you could take "a shallow copy" of it with Array#slice and then map the check of the items.
var array = [1, 2, 8, 3, 9, 88, 67],
sorted = array.slice().sort((a, b) => a - b),
position = array.map((a, i) => a === sorted[i]);
console.log(sorted);
console.log(position);
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to your question,
you just need to identify
If the array is in ascending order
To do that just apply some simple logic and we do not want to compare it with a sorted array.
This is the speediest method since it will break the loop on wrong condition.
let arr = [1,2,8,3,9,88,67];
let is_sorted = true;
for(let i=0, length = arr.length; i < length - 1; i++){
if(Number(arr[i]) > Number(arr[i+1])){
is_sorted = false;
break;
}
}
console.log("Is Sorted: ", is_sorted);
This is an XY problem – ie, you want "... to check if the array is in ascending order" and you tried sorting (via Array.prototype.sort) and then you're checking the result using binary operator ==. The result isn't what you expect, so you ask about the sort instead of keeping the focus on your actual goal: checking for ascending order
A case for why you shouldn't sort
Imagine a large array of hundreds or thousands or items. Is the following array in ascending order?
isAscending ([ 5, 1, ... thousands more items ... ])
// => true or false ?
Of course it's false because 1 is less than 5; the array is not in ascending order. Did we have to sort the entire array to arrive at that answer? No, we only needed to look at the first 2 items (in this case) to know the answer is false
Other answers show using .slice to copy the input array – this is silly tho, because of course we don't have to copy an array to determine if it is in ascending order – it's a waste of time/space.
A case for why you shouldn't use ==
Well first, you can't, because of the following (in JavaScript)
[ 1, 2, 3 ] == [ 1, 2, 3 ]
// => false
So how would use == if you could? Maybe an approach would be to check if each element in the first array is equal to each array element in the second array. Ok, now imagine two large arrays of hundreds of thousands of items. Are the following two arrays equal?
[ 1, ... thousands of items ... ] == [ 2, ... thousands of items ... ]
// => true or false ?
We know they're not equal because 1 != 2 – it's the same case as the sorting; there's no sense in comparing the rest of the items because we already know the arrays are not equal
There's other ways to compare Arrays (and objects) in JavaScript, but we only have one input array, so there's no array to compare it to – this is another dead-end to the approach
Check if an array is in ascending order
Ok, so now that were done talking about array sorting and array equality in JavaScript, we can actually write a function that does what you intend it to do
const isAscending = ([x,y,...rest]) =>
x === undefined // array is empty
? true
: y === undefined // single element array
? true
: x > y // first element is larger than second
? false
: isAscending ([y,...rest]) // check remaining elements
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
Stack-safe recursion
But you have to be careful using recursion in JavaScript – input arrays of just a couple thousand elements could cause a stack overflow in the program above
Using a constant-space looping mechanism, we can rewrite isAscending in a way that works on arrays of any input size. The loop/recur interface gives us an opportunity to track any number/type of state variables, so this implementation also avoids the costly creation of many array slices (in rest parameter and spread argument syntax above)
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const isAscending = xs =>
xs.length < 2
? true
: loop ((a = 0, b = 1) =>
a === xs.length
? true
: xs [a] > xs [b]
? false
: recur (a + 1, b + 1)) // *
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
* or recur (b, b + 1), which saves on addition operation per array element
The sort method applied to an array will modify the array itself. So it's logic to have the first array sorted and both arr and newArr will be equal.
To test if the array is in ascending order you may loop through it and check if there is and index i where arr[i+1] < arr[i]
let arr = [1,2,8,3,9,88,67];
let test=true;
for (var i = 1; i < arr.length; i++) {
if(arr[i]<arr[i-1]) {
test=false;
break;
}
}
console.log(test);

Finding unique values in multiple arrays

I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
 UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)

javascript sort list of lists by sublist second entry

How can I sort a list of lists by order of the last element?
Here is my best attempt so far:
var mylist = [ [1, 'c'], [3, 'a'], [5, 'b']];
mylist.sort(function(a,b){return a[1]-b[1];});
My call to sort has not effect, and mylist remains in the same order.
var mylist = [ [1, 'c'], [3, 'a'], [5, 'b']];
mylist.sort(function(a,b){return a[1].localeCompare(b[1]);});
console.log(mylist)
Use a[1].localeCompare(b[1]) to sort the list in the ascending order or b[1].localeCompare(a[1]) for other way round. This way you will be comparing two strings. You were trying to subtract one string from the other which return NaN.
var mylist = [ [1, 'c'], [3, 'a'], [5, 'b']];
// to sort by 1st NUMBER element; minus also used for object such as date
mylist.sort(function(a,b){return a[0] - b[0];});
// to sort by 2nd STRING element with >.
// strings can be compared based on alphabetical order with > or <,
// but not with a math order operator -, which is supposed to used for comparison between number or date.
mylist.sort(function(a,b){return a[1] > b[1];});
Above mentioned examples gives sorting in ascending order, here I note what I understand for who may need:
to sort in ascending order [1,9,7] to [9,7,1], we could image there is an empty array[], then we'll just put each element one by one to this array in the right order (based on comparison with existing elements).
function(a,b) { return b - a; }
here a is existing one, b is the one we are going to put in the array.
if return > 0 (true) >> [b,a] , we could consider the return value as the order of a, positive/true means a should put later, that's the sort() means.
if return <=0 (false) >> [a,b]
for [1,9,7], the loop will be:
[]
// to put 1
[1]
// to put 9
a = 1, b = 9, b - a > 0 ==> [9,1]
// to put 7
a = 9, b = 7, b - a < 0 ==> [9,7,1]
a = 1, b = 7, b - a > 0 ==> [9,7,1]

How to insert an item into an array at a specific index (JavaScript)

I am looking for a JavaScript array insert method, in the style of:
arr.insert(index, item)
Preferably in jQuery, but any JavaScript implementation will do at this point.
You want the splice function on the native array object.
arr.splice(index, 0, item); will insert item into arr at the specified index (deleting 0 items first, that is, it's just an insert).
In this example we will create an array and add an element to it into index 2:
var arr = [];
arr[0] = "Jani";
arr[1] = "Hege";
arr[2] = "Stale";
arr[3] = "Kai Jim";
arr[4] = "Borge";
console.log(arr.join()); // Jani,Hege,Stale,Kai Jim,Borge
arr.splice(2, 0, "Lene");
console.log(arr.join()); // Jani,Hege,Lene,Stale,Kai Jim,Borge
You can implement the Array.insert method by doing this:
Array.prototype.insert = function ( index, ...items ) {
this.splice( index, 0, ...items );
};
Then you can use it like:
var arr = [ 'A', 'B', 'E' ];
arr.insert(2, 'C', 'D');
// => arr == [ 'A', 'B', 'C', 'D', 'E' ]
Other than splice, you can use this approach which will not mutate the original array, but it will create a new array with the added item. It is useful, when you need to avoid mutation. I'm using the ES6 spread operator here.
const items = [1, 2, 3, 4, 5]
const insert = (arr, index, newItem) => [
// part of the array before the specified index
...arr.slice(0, index),
// inserted item
newItem,
// part of the array after the specified index
...arr.slice(index)
]
const result = insert(items, 1, 10)
console.log(result)
// [1, 10, 2, 3, 4, 5]
This can be used to add more than one item by tweaking the function a bit to use the rest operator for the new items, and spread that in the returned result as well:
const items = [1, 2, 3, 4, 5]
const insert = (arr, index, ...newItems) => [
// part of the array before the specified index
...arr.slice(0, index),
// inserted items
...newItems,
// part of the array after the specified index
...arr.slice(index)
]
const result = insert(items, 1, 10, 20)
console.log(result)
// [1, 10, 20, 2, 3, 4, 5]
Custom array insert methods
1. With multiple arguments and chaining support
/* Syntax:
array.insert(index, value1, value2, ..., valueN) */
Array.prototype.insert = function(index) {
this.splice.apply(this, [index, 0].concat(
Array.prototype.slice.call(arguments, 1)));
return this;
};
It can insert multiple elements (as native splice does) and supports chaining:
["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(1, 6);
// ["b", "X", "Y", "Z", "c"]
2. With array-type arguments merging and chaining support
/* Syntax:
array.insert(index, value1, value2, ..., valueN) */
Array.prototype.insert = function(index) {
index = Math.min(index, this.length);
arguments.length > 1
&& this.splice.apply(this, [index, 0].concat([].pop.call(arguments)))
&& this.insert.apply(this, arguments);
return this;
};
It can merge arrays from the arguments with the given array and also supports chaining:
["a", "b", "c", "d"].insert(2, "V", ["W", "X", "Y"], "Z").join("-");
// "a-b-V-W-X-Y-Z-c-d"
DEMO: http://jsfiddle.net/UPphH/
Using Array.prototype.splice() is an easy way to achieve it
const numbers = ['one', 'two', 'four', 'five']
numbers.splice(2, 0, 'three');
console.log(numbers)
Read more about Array.prototype.splice
If you want to insert multiple elements into an array at once check out this Stack Overflow answer: A better way to splice an array into an array in javascript
Also here are some functions to illustrate both examples:
function insertAt(array, index) {
var arrayToInsert = Array.prototype.splice.apply(arguments, [2]);
return insertArrayAt(array, index, arrayToInsert);
}
function insertArrayAt(array, index, arrayToInsert) {
Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
return array;
}
Finally here is a jsFiddle so you can see it for yourself: http://jsfiddle.net/luisperezphd/Wc8aS/
And this is how you use the functions:
// if you want to insert specific values whether constants or variables:
insertAt(arr, 1, "x", "y", "z");
// OR if you have an array:
var arrToInsert = ["x", "y", "z"];
insertArrayAt(arr, 1, arrToInsert);
Solutions & Performance
Today (2020.04.24) I perform tests for chosen solutions for big and small arrays. I tested them on macOS v10.13.6 (High Sierra) on Chrome 81.0, Safari 13.1, and Firefox 75.0.
Conclusions
For all browsers
surprisingly for small arrays, non-in-place solutions based on slice and reduce (D,E,F) are usually 10x-100x faster than in-place solutions
for big arrays the in-place-solutions based on splice (AI, BI, and CI) was fastest (sometimes ~100x - but it depends on the array size)
for small arrays the BI solution was slowest
for big arrays the E solution was slowest
Details
Tests were divided into two groups: in-place solutions (AI, BI, and CI) and non-in-place solutions (D, E, and F) and was performed for two cases:
test for an array with 10 elements - you can run it here
test for an array with 1,000,000 elements - you can run it here
Tested code is presented in the below snippet:
jsfiddle
function AI(arr, i, el) {
arr.splice(i, 0, el);
return arr;
}
function BI(arr, i, el) {
Array.prototype.splice.apply(arr, [i, 0, el]);
return arr;
}
function CI(arr, i, el) {
Array.prototype.splice.call(arr, i, 0, el);
return arr;
}
function D(arr, i, el) {
return arr.slice(0, i).concat(el, arr.slice(i));
}
function E(arr, i, el) {
return [...arr.slice(0, i), el, ...arr.slice(i)]
}
function F(arr, i, el) {
return arr.reduce((s, a, j)=> (j-i ? s.push(a) : s.push(el, a), s), []);
}
// -------------
// TEST
// -------------
let arr = ["a", "b", "c", "d", "e", "f"];
let log = (n, f) => {
let a = f([...arr], 3, "NEW");
console.log(`${n}: [${a}]`);
};
log('AI', AI);
log('BI', BI);
log('CI', CI);
log('D', D);
log('E', E);
log('F', F);
This snippet only presents tested code (it not perform tests)
Example results for a small array on Google Chrome are below:
For proper functional programming and chaining purposes, an invention of Array.prototype.insert() is essential. Actually, the splice could have been perfect if it had returned the mutated array instead of a totally meaningless empty array. So here it goes:
Array.prototype.insert = function(i,...rest){
this.splice(i,0,...rest)
return this
}
var a = [3,4,8,9];
document.write("<pre>" + JSON.stringify(a.insert(2,5,6,7)) + "</pre>");
Well, OK, the above with the Array.prototype.splice() one mutates the original array and some might complain like "you shouldn't modify what doesn't belong to you" and that might turn out to be right as well. So for the public welfare, I would like to give another Array.prototype.insert() which doesn't mutate the original array. Here it goes;
Array.prototype.insert = function(i,...rest){
return this.slice(0,i).concat(rest,this.slice(i));
}
var a = [3,4,8,9],
b = a.insert(2,5,6,7);
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
You can use splice() for this
The splice() method usually receives three arguments when adding an element:
The index of the array where the item is going to be added.
The number of items to be removed, which in this case is 0.
The element to add.
let array = ['item 1', 'item 2', 'item 3']
let insertAtIndex = 0
let itemsToRemove = 0
array.splice(insertAtIndex, itemsToRemove, 'insert this string on index 0')
console.log(array)
I recommend using pure JavaScript in this case. Also there isn't any insert method in JavaScript, but we have a method which is a built-in Array method which does the job for you. It's called splice...
Let's see what's splice()...
The splice() method changes the contents of an array by removing
existing elements and/or adding new elements.
OK, imagine we have this array below:
const arr = [1, 2, 3, 4, 5];
We can remove 3 like this:
arr.splice(arr.indexOf(3), 1);
It will return 3, but if we check the arr now, we have:
[1, 2, 4, 5]
So far, so good, but how we can add a new element to array using splice?
Let's put back 3 in the arr...
arr.splice(2, 0, 3);
Let's see what we have done...
We use splice again, but this time for the second argument, we pass 0, meaning we don't want to delete any item, but at the same time, we add a third argument which is the 3 that will be added at second index...
You should be aware that we can delete and add at the same time. For example, now we can do:
arr.splice(2, 2, 3);
Which will delete two items at index 2. Then add 3 at index 2 and the result will be:
[1, 2, 3, 5];
This is showing how each item in splice work:
array.splice(start, deleteCount, item1, item2, item3 ...)
Here are two ways:
const array = [ 'My', 'name', 'Hamza' ];
array.splice(2, 0, 'is');
console.log("Method 1: ", array.join(" "));
Or
Array.prototype.insert = function ( index, item ) {
this.splice( index, 0, item );
};
const array = [ 'My', 'name', 'Hamza' ];
array.insert(2, 'is');
console.log("Method 2 : ", array.join(" "));
Append a single element at a specific index
// Append at a specific position (here at index 1)
arrName.splice(1, 0,'newName1');
// 1: index number, 0: number of element to remove, newName1: new element
// Append at a specific position (here at index 3)
arrName[3] = 'newName1';
Append multiple elements at a specific index
// Append from index number 1
arrName.splice(1, 0, 'newElemenet1', 'newElemenet2', 'newElemenet3');
// 1: index number from where append start,
// 0: number of element to remove,
//newElemenet1,2,3: new elements
Array#splice() is the way to go, unless you really want to avoid mutating the array. Given 2 arrays arr1 and arr2, here's how you would insert the contents of arr2 into arr1 after the first element:
const arr1 = ['a', 'd', 'e'];
const arr2 = ['b', 'c'];
arr1.splice(1, 0, ...arr2); // arr1 now contains ['a', 'b', 'c', 'd', 'e']
console.log(arr1)
If you are concerned about mutating the array (for example, if using Immutable.js), you can instead use slice(), not to be confused with splice() with a 'p'.
const arr3 = [...arr1.slice(0, 1), ...arr2, ...arr1.slice(1)];
Another possible solution, with usage of Array.reduce.
const arr = ["apple", "orange", "raspberry"];
const arr2 = [1, 2, 4];
const insert = (arr, item, index) =>
arr.reduce(function(s, a, i) {
i === index ? s.push(item, a) : s.push(a);
return s;
}, []);
console.log(insert(arr, "banana", 1));
console.log(insert(arr2, 3, 2))
Even though this has been answered already, I'm adding this note for an alternative approach.
I wanted to place a known number of items into an array, into specific positions, as they come off of an "associative array" (i.e. an object) which by definition is not guaranteed to be in a sorted order. I wanted the resulting array to be an array of objects, but the objects to be in a specific order in the array since an array guarantees their order. So I did this.
First the source object, a JSONB string retrieved from PostgreSQL. I wanted to have it sorted by the "order" property in each child object.
var jsonb_str = '{"one": {"abbr": "", "order": 3}, "two": {"abbr": "", "order": 4}, "three": {"abbr": "", "order": 5}, "initialize": {"abbr": "init", "order": 1}, "start": {"abbr": "", "order": 2}}';
var jsonb_obj = JSON.parse(jsonb_str);
Since the number of nodes in the object is known, I first create an array with the specified length:
var obj_length = Object.keys(jsonb_obj).length;
var sorted_array = new Array(obj_length);
And then iterate the object, placing the newly created temporary objects into the desired locations in the array without really any "sorting" taking place.
for (var key of Object.keys(jsonb_obj)) {
var tobj = {};
tobj[key] = jsonb_obj[key].abbr;
var position = jsonb_obj[key].order - 1;
sorted_array[position] = tobj;
}
console.dir(sorted_array);
Immutable insertion
Using the splice method is surely the best answer if you need to insert into an array in-place.
However, if you are looking for an immutable function that returns a new updated array instead of mutating the original array on insert, you can use the following function.
function insert(array, index) {
const items = Array.prototype.slice.call(arguments, 2);
return [].concat(array.slice(0, index), items, array.slice(index));
}
const list = ['one', 'two', 'three'];
const list1 = insert(list, 0, 'zero'); // Insert single item
const list2 = insert(list, 3, 'four', 'five', 'six'); // Insert multiple
console.log('Original list: ', list);
console.log('Inserted list1: ', list1);
console.log('Inserted list2: ', list2);
Note: This is a pre-ES6 way of doing it, so it works for both older and newer browsers.
If you're using ES6 then you can try out rest parameters too; see this answer.
Anyone who's still having issues with this one and have tried all the options in previous answers and never got it. I'm sharing my solution, and this is to take into consideration that you don't want to explicitly state the properties of your object vs the array.
function isIdentical(left, right){
return JSON.stringify(left) === JSON.stringify(right);
}
function contains(array, obj){
let count = 0;
array.map((cur) => {
if(this.isIdentical(cur, obj))
count++;
});
return count > 0;
}
This is a combination of iterating the reference array and comparing it to the object you wanted to check, converting both of them into a string, and then iterating if it matched. Then you can just count. This can be improved, but this is where I settled.
Taking profit of the reduce method as follows:
function insert(arr, val, index) {
return index >= arr.length
? arr.concat(val)
: arr.reduce((prev, x, i) => prev.concat(i === index ? [val, x] : x), []);
}
So in this way we can return a new array (will be a cool functional way - more much better than using push or splice) with the element inserted at index, and if the index is greater than the length of the array it will be inserted at the end.
I tried this and it is working fine!
var initialArr = ["India","China","Japan","USA"];
initialArr.splice(index, 0, item);
Index is the position where you want to insert or delete the element.
0, i.e., the second parameter, defines the number of elements from the index to be removed.
item contains the new entries which you want to make in the array. It can be one or more than one.
initialArr.splice(2, 0, "Nigeria");
initialArr.splice(2, 0, "Australia","UK");
I have to agree with Redu's answer because splice() definitely has a bit of a confusing interface. And the response given by cdbajorin that "it only returns an empty array when the second parameter is 0. If it's greater than 0, it returns the items removed from the array" is, while accurate, proving the point.
The function's intent is to splice or as said earlier by Jakob Keller, "to join or connect, also to change.
You have an established array that you are now changing which would involve adding or removing elements...." Given that, the return value of the elements, if any, that were removed is awkward at best. And I 100% agree that this method could have been better suited to chaining if it had returned what seems natural, a new array with the spliced elements added. Then you could do things like ["19", "17"].splice(1,0,"18").join("...") or whatever you like with the returned array.
The fact that it returns what was removed is just kind of nonsense IMHO. If the intention of the method was to "cut out a set of elements" and that was its only intent, maybe. It seems like if I don't know what I'm cutting out already though, I probably have little reason to cut those elements out, doesn't it?
It would be better if it behaved like concat(), map(), reduce(), slice(), etc. where a new array is made from the existing array rather than mutating the existing array. Those are all chainable, and that is a significant issue. It's rather common to chain array manipulation.
It seems like the language needs to go one or the other direction and try to stick to it as much as possible. JavaScript being functional and less declarative, it just seems like a strange deviation from the norm.
I like a little safety and I use this:
Array.prototype.Insert = function (item, before) {
if (!item) return;
if (before == null || before < 0 || before > this.length - 1) {
this.push(item);
return;
}
this.splice(before, 0, item);
}
var t = ["a", "b"]
t.Insert("v", 1)
console.log(t)
You can do it with array.splice:
/**
* #param arr: Array
* #param item: item to insert
* #param index: index at which to insert
* #returns array with the inserted element
*/
export function _arrayInsertAt<T>(arr: T[], item: T, index: number) {
return arr.splice(index, 0, item);;
}
Doc of array.slice
Here's a working function that I use in one of my applications.
This checks if an item exists:
let ifExist = (item, strings = [ '' ], position = 0) => {
// Output into an array with an empty string. Important just in case their isn't any item.
let output = [ '' ];
// Check to see if the item that will be positioned exist.
if (item) {
// Output should be equal to an array of strings.
output = strings;
// Use splice() in order to break the array.
// Use positional parameters to state where to put the item
// and 0 is to not replace an index. Item is the actual item we are placing at the prescribed position.
output.splice(position, 0, item);
}
// Empty string is so we do not concatenate with comma or anything else.
return output.join("");
};
And then I call it below.
ifExist("friends", [ ' ( ', ' )' ], 1)} // Output: ( friends )
ifExist("friends", [ ' - '], 1)} // Output: - friends
ifExist("friends", [ ':'], 0)} // Output: friends:
Here is the modern (Typescript functional) way:
export const insertItemInList = <T>(
arr: T[],
index: number,
newItem: T
): T[] => [...arr.slice(0, index), newItem, ...arr.slice(index)]
I do it like so:
const insert = (what, where, index) =>
([...where.slice(0, index), what , ...where.slice(index, where.length)]);
const insert = (what, where, index) =>
([...where.slice(0, index), what , ...where.slice(index, where.length)]);
const list = [1, 2, 3, 4, 5, 6];
const newList = insert('a', list, 2);
console.log(newList.indexOf('a') === 2);
Here's a simple function that supports inserting multiple values at the same time:
function add_items_to_array_at_position(array, index, new_items)
{
return [...array.slice(0, index), ...new_items, ...array.slice(index)];
}
Usage example:
let old_array = [1,2,5];
let new_array = add_items_to_array_at_position(old_array, 2, [3,4]);
console.log(new_array);
//Output: [1,2,3,4,5]
var array= [10,20,30,40]
var i;
var pos=2; //pos=index + 1
/*pos is position which we want to insert at which is index + 1.position two in an array is index 1.*/
var value=5
//value to insert
//Initialize from last array element
for(i=array.length-1;i>=pos-1;i--){
array[i+1]=array[i]
}
array[pos-1]=value
console.log(array)
Multi purpose for ARRAY and ARRAY OF OBJECT reusable approach
let arr = [0,1,2];
let obj = [{ name: "abc"},{ name: "xyz"},{ name: "ijk"} ];
const addArrayItemAtIndex = ( array, index, newItem ) => {
return [...array.slice(0, index), newItem, ...array.slice(index)];
}
// For Array
console.log( addArrayItemAtIndex(arr, 2, 159 ) );
// For Array of Objects
console.log( addArrayItemAtIndex(obj, 0, { name: "AMOOS"} ) );

Categories