I have a JavaScript array of length 3
arr = [1, 3, 8];
Is there a way to add elements to arr while I am iterating over it? I want to insert two values 10 and 20 into the existing arr. Can I do it like this? The newly added elements, 10 and 20, must also be looped over in the same for in loop.
for(var i in arr) {
if( i == 0 ) {
arr[length + 0] = 10;
arr[length + 1] = 20;
}
}
Or, what is the correct way to add elements to an array while iterating and making sure that the newly added elements will also be looped over?
You could use a for statement and check the length while iterating the array.
With for ... in statement, iteration of new elements is not granted.
Properties added to the object over which iteration is occurring may either be visited or omitted from iteration. In general it is best not to add, modify or remove properties from the object during iteration, other than the property currently being visited.
var array = [1, 3, 8],
i;
for (i = 0; i < array.length; i++) {
console.log(array[i]);
if (i === 0) {
array.push(10, 20);
}
}
You really shouldn't ever need to do this, there's probably a better method. But, if you absolutely have to, use a while loop instead:
var arr = [1, 2, 3];
var i = 0;
while (i < arr.length) {
if( i == 0 ) {
arr.push(10, 20);
}
console.log(arr[i]); // 1, 2, 3, 10, 20
i++;
}
Related
Good day, I have been trying to solve JS problem where I have an array a = [1, 1, 1, 2, 1, 3, 4]. I have to loop through an array and remove every three elements ie (1,1,1) do some logic, then the next three (1,1,2), and so on.
I have used for loop and splice
a = [1, 1, 1, 2, 1, 3, 4]
tuple = ()
for(j=0; j < a.length; j++){
tuple = a.splice(j, 3)
}
However, loop would not go beyond first round of (1,1,1).
What would be the correct way to loop through this array and remove sets of every three elements.
thank you.
Splice return removed elements from base array in given range. You probable want to use slice which only copy them and doesn't change primary array. You can also check this solution.
let a = [1, 1, 1, 2, 1, 3, 4];
for (j = 0; j < a.length; j++) {
if ( j < a.length - 2 ) {
const touple = [ a[j], a[j+1], a[j+2] ]
console.log( touple )
}
}
splice changes the original array and that must be causing your issue.
So, you can use slice which returns a shallow copy of a portion of the original array (without modifying the original array):
const a = [1, 1, 1, 2, 1, 3, 4]
const doLogic = (arr) => {
console.log(JSON.stringify(arr)) // JSON.stringify is only for one-liner print. You can ignore it.
}
for (let i = 0; i < a.length - 2; i++) {
const picked = a.slice(i, i + 3)
doLogic(picked)
}
Or this, if you want to pick the full array when length is less than 3:
if (a.length < 3) {
doLogic(a)
} else {
for (let i = 0; i < a.length - 2; i++) {
const picked = a.slice(i, i + 3)
doLogic(picked)
}
}
I'm looking for an efficient JavaScript utility method that in O(n) will remove a set of items from an array in place. You can assume equality with the === operator will work correctly.
Here is an example signature (written in TypeScript for type clarity)
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) { ... }
My thought is to do this in two passes. The first pass gets the indexes that need to be removed. The second pass then compacts the array by copying backwards from the current index being removed through the next index being removed.
Does anyone have code like this already handy or a more efficient way to do this?
P.S. Please don't point out the filter function as that creates a copy of the array, it does not work in place.
Iterate over the array, copying elements that aren't in itemsToDelete to the next destination index in the array. When you delete an element, you don't increment this index.
Finally, reset the length of the array at the end to get rid of the remaining elements at the end.
function deleteItemsFromArray(array, itemsToDelete) {
let destIndex = 0;
array.forEach(e => {
if (!itemsToDelete.includes(e)) {
array[destIndex++] = e;
}
});
array.length = destIndex;
}
const array = [1, 2, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [3, 5]);
console.log(array);
function deleteItems(array,items){
let itemsToDelete=0;
let indexToSwap = array.length-1;
for(let i = array.length-1,currItem ; i>=0 ; i--){
if(items.includes(array[i]){
[array[i] , array[indexToSwap]] = [array[indexToSwap] , array[i]];
--indexToSwap;
++itemsToDelete;
}
}
array.splice(array.length-itemsToDelete,itemsToDelete);
}
This should work, I haven't tested it.
The idea is to swap the elements to delete to the end of the array. You can remove them at the end how I do in my code or you could use too the pop() function every time.
It's very, very simple - transform itemsToDelete to a Set. This ensures O(1) lookups. Then walk through the array backwards and remove items with Array#splice. In total, that gives you a linear O(n+m) time complexity:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) {
const setToDelete = new Set(itemsToDelete);
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = setToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
You can save the conversion step if you just make the function accept a set to begin with and change the signature to:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: Set<T>)
function deleteItemsFromArray(array, itemsToDelete) {
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = itemsToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
deleteItemsFromArray(
arr, new Set([1, 3, 5, 7, 8, 9])
)
console.log(arr);
Using copyWithin method will be helpful here.
method 1
Traverse from last index to first index, when ever we find the item to remove just copy/move elements to left.
Though we will be doing times the copy/move, But still have some unnecessary moving elements.
method 2
Traverse from first index to last index, when ever we find the item to remove, identify the group of elements to copy/move. This will avoid the unnecessary moving elements as in method 1.
function deleteItemsFromArray(array, delete_list) {
for (let i = array.length - 1; i > -1; i--) {
if (delete_list.includes(array[i])) {
array.copyWithin(i, i + 1).pop();
}
}
}
// Alternate way, with minimal copy/move group of elements.
function deleteItemsFromArrayAlternate(array, delete_list) {
let index = -1;
let count = 0;
for (let i = 0; i <= array.length; i++) {
if (delete_list.includes(array[i]) || !(i in array)) {
if (index > -1) {
array.copyWithin(index - count + 1, index + 1, i);
}
count += 1;
index = i;
}
}
array.length = array.length - delete_list.length;
}
const array = [9, 12, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [9, 6, 7]);
console.log(array);
I am dynamically slicing an array, and I can get the functionality I want by simply removing the last element with arr.pop() but I want to know exactly why my while loop is adding this to my array when it breaks my conditional.
slices(num){
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num){
throw new Error('Slice size is too big.')
} else {
while (arr[i].length === num){
i++
arr.push(this.digits.slice(i, num + i))
}
// arr.pop() - removed for testing
return arr
}
}
Here is an example. Let's say we want to slice this array:
this.digits = [ 3, 1, 0, 0, 1 ]
Ideally, our output will look like this:
[3, 1, 0], [1, 0, 0], [0, 0, 1]]
With my current code and without using arr.pop(), my algorithim will consistently sneak in an extra slice iteration that has less length than what my conditional is asking for (in this case, num == 3)
This will be my output:
[[3, 1, 0], [1, 0, 0], [0, 0, 1], [0, 1]]
I know there are many ways to do this, but for this, I would like to maintain the integrity of my code, so a solution that uses my implementation would be great :D
EDIT: I get why the last element is being added. Since the element before fulfills the conditional (it's length is equal to num), it moves on to the next iteration but how I do handle it eloquently without using .pop()
EDIT: Thanks all for the answers! They all seem like they would work, but Peter B's implementation was so very clean, especially given that he changed just a few lines for me and it worked like a charm. Thanks again!
You are checking the wrong condition in the while. It is better to calculate how many sub-arrays you are going to add inside the while (in addition to the one that you start with), and count up to that number, like this:
var digits = [3, 1, 0, 0, 1];
function slices(num) {
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
var sliceCutoff = this.digits.length - num;
while (i < sliceCutoff) {
i++
arr.push(this.digits.slice(i, num + i))
}
return arr
}
}
console.log(slices(3));
You need to check if the leftover items are enough to get an array with the wanted length. That means, you need a single loop with a continuing check for the actual ster index, wanted size and the length.
An approach by checking the last array after splicing is unnecessary, because it generates an avoidable overhead.
function slice(array, n) {
var result = [],
start = 0;
while (start + n <= array.length) {
result.push(array.slice(start, start++ + n));
}
return result;
}
var array = [3, 1, 0, 0, 1];
console.log(slice(array, 3));
Description
I believe that you're looking for something along the lines of this? As you can see, I've also removed some redundant code, i.e. using a while loop in this scenario and the else clause.
I've also just declared digits as a parameter for this demo, I believe that you'd have the initiative to be able to change this to your application(s) requirement(s) without much/any assistance.
function slices(digits, num) {
const arr = [];
if (digits.length < num)
throw new Error('Slice size is too big.')
for (let i = 0; i != num; i++)
arr.push(digits.slice(i, num + i));
return arr;
}
var d = [3, 1, 0, 0, 1]; // Only here for the demo.
console.log(slices(d, 3));
You're really close. I think my proposed solution here keeps the general idea of yours. The problem you're hitting is that checking arr[i].length being equal to num means this is only checking the last item you added to the array, not the next one. Instead, check the item you're about to add.
this.digits = [ 3, 1, 0, 0, 1 ];
function slices(num) {
let arr = []
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
while (this.digits.slice(i, num + i).length === num){
arr.push(this.digits.slice(i, num + i))
i++
}
// arr.pop() - removed for testing
return arr
}
}
console.log(slices(3));
I have the following structure:
objet and arrays
I would like to determine if the object has an array with values and if so, save those values by saving those values in another arryar that I will then go through, I have the following up to now:
if (resp.Boutique.length >= 1) {
for (let index = 0; index < resp.Boutique.length; index++) {
this.general.push(resp.Boutique[index]);
}
}
if (resp.Curso.length >= 1) {
for (let index = 0; index < resp.Curso.length; index++) {
this.general.push(resp.Curso[index]);
}
}
if (resp.Deporte.length >= 1) {
for (let index = 0; index < resp.Deporte.length; index++) {
this.general.push(resp.Deporte[index]);;
}
}
but if you have more 'if' elements, I do not think it's the best way.
Any recommendation?
If the properties always contain arrays, then there's no need to test for their length before iterating over them; if they have length 0, no iterations will be performed, so nothing will be pushed to this.general.
But there's a better way - you can simply spread each possibly-empty array into this.general, in a single line:
this.general = [...resp.Boutique, ...resp.Curso, ...resp.Deporte];
If the properties of resp contain only the arrays you want to include in .general, then iterate over the values of resp instead:
this.general = [].concat(...Object.values(resp));
arr.concat(arr1, arr2, arr3 ...)
let arr = [1, 2, 3, 4];
let arr1 = [4, 5, 6, 7];
let arr2 = [7, 8, 9, 10]
console.log(arr.concat(arr1, arr2));
I am trying to get this function to split an array into subsets. each subset is to have numbers that are equal to the previous or within 1 from the previous number.
The example I have below should return two subsets but it returns {0, 1, 2, 3} instead. Any idea on what I am doing wrong? Also, is there a better way to dynamically create an array for each new subset? Thanks
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function(a, b){return a-b});
for(var i = 0; i<myarr.length; i++){
var iplus = i+1;
if(i === i || i === iplus){
newArr= [];
newArr.push(i);
}else if (i !== i || i !== iplus){
arr2 =[];
arr2.push(i);
}
}
}
What you are trying to do is usually called "partitioning". The generic version of the problem is to partition an array into sub-arrays using some "rule", or predicate, or condition, which specifies which partition a particular element is supposed to go into, or specifies that it should go into a new partition.
The pseudo code for doing this would be:
To partition an array:
Initialize the resulting array
For each element in the array
If that element starts a new chunk
Create a new empty chunk in the resulting array
Add the element to the most recent chunk
Return the result
This can be expressed in JS quite straightforwardly as
function partition(array, fn) {
return array.reduce((result, elt, i, a) => {
if (!i || !fn(elt, i, a)) result.push([]);
result[result.length - 1].push(elt);
return result;
}, []);
}
Now we need to write the function saying when a new partition should start:
// Is the element within one of the previous element?
function close(e, i, a) {
return Math.abs(e - a[i-1]) > 1;
}
We can now partition the array with
partition([[4, 13, 2, 3], close)
This should work.
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function (a, b) { return a - b });
arrSubsets = [];
arr1 = [];
for (var i = 0; i < myarr.length; i++) {
if (myarr[i - 1] === undefined) {
arr1.push(myarr[i]);
continue;
}
if (myarr[i] - myarr[i - 1] <= 1) {
arr1.push(myarr[i]);
}
else {
arrSubsets.push(arr1);
arr1 = [];
arr1.push(myarr[i]);
}
}
if (arr1.length > 0)
arrSubsets.push(arr1);
}
max_tickets();
Based on your questions:
Any idea on what I am doing wrong?.
Inside of your loop you are using i as if it is the value of the array, but the loop goes from 0 to the value of myarr.length in your particular case 4, so that makes the value of i to be 0, 1, 2, 3.
As you can see you are using the values of the index to compare, instead of using the values of the array in order to use the values of the array you must specify the arrayname[index], in your case myarr[i] that will give you the values: 4, 13, 2, 3.
Also, is there a better way to dynamically create an array for each new subset?
Yes you can create an array inside of another array dynamically inside of a loop:
var b = [];
for(var i = 0; i < 10; i++){
b.push(['I am' + i, i]);
}
As you can see in the previous example I'm creating an array inside of the b array so once the loop finishes the b array will have 10 arrays inside of it with 2 elements each.