Splitting and Adding Arrays - javascript

Trying to remove the last two elements then add a 2 to the end of the array. But, keep getting an error. First test works then my second fails.
var userArray = [4, 2, 8, 5, 0, 1, 6]; // Tests may use different array values
/* Your solution goes here */
userArray.splice(5, 2, 2);
CORRECT: Testing the final value of userArray when the initial array is [4, 2, 8, 5, 0, 1, 6]
Yours
4,2,8,5,0,2
INCORRECT: Testing the final value of userArray when the initial array is [-5, 3]
Yours and expected differ. See highlights below.
Yours
-5,3,2
Expected
2

// Tests may use different array values
Your answer should be: userArray.splice(userArray.length-2, 2, 2);

var arr = [1, 2, 3, 4, 5, 3, 1]
arr.splice(-2, 2, 2)
console.log(arr)

tried this and workd.. had to ensure the array had more than 2 elements first..
function trimLast2elements(ar){
if(ar.length > 2){
index = ar.length - 2 //get first index of last 2 elements..
ar.splice(index, 2, 2);
return ar; //return array.
}else{
//function if array count is less
}
}
apply function when needed.

Related

Why .splice() method deletes elements of different indexes?

This is my first question on stackoverflow, I am new :) learning JS. I have a question. I wrote this function:
function inverseSlice(items, a, b) {
return items.splice(a, b);
}
inverseSlice([1, 2, 3, 4, 5, 6], 2, 4)
(4) [3, 4, 5, 6]
Why this function returns last 4 digits, when according to docs on MDN (which I read 10 times already :P) splice() method should remove here only 2 middle ones (3, 4)? It should return [1, 2, 5, 6]. Am I right? Thank You for all Your help :)
It's doing exactly what it advertises, it "returns an array containing the deleted elements."
function inverseSlice(items, a, b) {
return items.splice(a, b);
}
let array = [1, 2, 3, 4, 5, 6, 7, 8];
// Deletes 4 entries starting at index 2,
// or in other words [3,4,5,6] are snipped
inverseSlice(array, 2, 4);
console.log(array);
Unless you keep a reference to the array you're passing in you'll never observe anything about how it ends up, you'll only get the deleted elements.
splice will
Mutate the original array: remove N items, where N is the third parameter, starting from the start index (first parameter) to the number specified (so here, it'll remove indicies 2 through 5 from the array; indicies 2, 3, 4, and 5, a total of 4 get removed)
Return the removed elements - so, here, that's [3, 4, 5, 6].
The original array is now [1, 2], but you're logging what was returned by .splice, not the original array.
If you wanted [1, 2, 5, 6], you'd want to specify 2 for the 3rd argument (2 items to remove), and then log the original array:
function inverseSlice(items, a, b) {
return items.splice(a, b);
}
const arr = [1, 2, 3, 4, 5, 6];
const removedItems = inverseSlice(arr, 2, 2);
console.log(arr);
console.log(removedItems);
What you are confused about is the arguments to splice, The two arguments that you pass to splice are not the start and end index but the start index and the count of items to be deleted.
Hence in your example it deleted items from 2 to 5 index and returned you the resultant array i.e [3, 4, 5, 6]
As per the docs:
Syntax:
let arrDeletedItems = arr.splice(start[, deleteCount[, item1[, item2[, ...]]]])
Parameters
Start : The index at which to start changing the array.
deleteCount: n integer indicating the number of elements in the array to remove from start.
item1, item2, ... : The elements to add to the array, beginning from start. If you do not specify any elements, splice() will only remove
elements from the array.

JavaScript ensure all numbers are unique and if not plus one (or more)

I'm trying to place some labels on a d3 chart.
I have a DATA object with a DATA.rowOffset value for each. Basically my code calculates the DATA.rowOffset and sets it like this: d.rowOffset = Math.floor(d.total/heightSquares); but sometimes the rowOffset is the same and so the labels render on top of each other.
I need to cycle through this DATA and check for duplicates and then just +1 any duplicates.
I tried a method that looked at the previous .rowOffset and then added 1 to the current rowOffset, but that doesn't work if there are more than 2 duplicates.
I'm sure there's an easier way.... perhaps.
Edit: here's some code I tried mainly if (d.rowOffset === DATA[i-1].rowOffset) d.rowOffset++; so it checks the previous row offset. I think I need to cycle through all the data and then restart the cycle if a duplicate is found.
DATA.forEach(function(d, i) {
d.amt = +d.amt;
d.units = Math.floor(d.amt / squareValue);
sumTotal = sumTotal + d.units;
d.total = sumTotal;
d.rowOffset = Math.floor(d.total / heightSquares);
if (i > 0) {
console.log(DATA[i - 1].rowOffset);
if (d.rowOffset === DATA[i - 1].rowOffset) d.rowOffset++;
}
Here's one approach you can take.
You initialize an empty Set data structure to keep track of unique values you've encountered so far. You iterate through the array and for each value do the following:
If the value was previously encountered, increment it until it doesn't match any previously encountered values
Update the encountered values to include this new value
Replace the old value with the new value
Here's how that would look in code:
function incrementDups(arr) {
// a set of encountered unique values
const encounters = new Set();
// increment each duplicate until it has no duplicates
return arr.map(num => {
while (encounters.has(num)) {
num += 1;
}
encounters.add(num);
return num;
});
}
console.log(
incrementDups(
[1, 2, 2, 3, 2, 2, 4] // [1, 2, 3, 4, 5, 6, 7]
)
);
console.log(
incrementDups(
[1, 1, 1, 1, 1, 1, 1] // [1, 2, 3, 4, 5, 6, 7]
)
);
console.log(
incrementDups(
[1, 99, 55, 4, 55, 2] // [1, 99, 55, 4, 56, 2]
)
);
The solution above has quadratic worst-case time complexity. The input that generates this situation is an array containing only duplicates, for example [1, 1, 1, 1], where the last iteration of the nested while loop will run N increments. Despite that, on average, this algorithm should perform quite well.
A further optimization could be made by using more space to remember the last increment value for a certain duplicate and use that as the start value for incrementing, rather than the number itself.
Right now, the code above actually does a fair amount of repetition. If we had [2, 2, 2, ...], for every 2 we would start incrementing from through 2, 3, 4, etc. even though technically the previous 2 already did our work for us. Ideally, we want the first 2 to start counting from 2, the second 2 to start counting from 3, etc. This will be particularly useful for large arrays of consecutive values. For example, if we had [1, 2, ... 99, ... 2, 2, 2, 2 /* 100 times */], using the first algorithm, each 2 would count from 2 to 99 plus how ever many increments to the next unique value. On the other hand, using this new approach only the first 2 would do this. The next 2 would just increment 99 to 100, the next one 100 to 101, and so on. If we were given an array of only duplicates as before, [1, 1, 1 ...], each 1 would only need to get incremented once now rather than going through the entire range.
This tightens the time complexity to O(N*max(array)) which is still quadratic but only depends on the range of values, not the number of duplicates like before. It is also more optimized for your particular situation since you will expect an array of low numbers that are close to each other in value.
To keep track of this info, we can use a Map of a number to the latest unique value it was incremented to.
function incrementDups(arr) {
// a set of encountered unique values
const encounters = new Set();
// a map of the last unique non-duplicate for each value
const lastNonDup = new Map();
// increment each duplicate until it has no duplicates
return arr.map(num => {
let updatedNum = lastNonDup.has(num) ? lastNonDup.get(num) : num;
while (encounters.has(updatedNum)) {
updatedNum += 1;
}
encounters.add(updatedNum);
lastNonDup.set(num, updatedNum);
return updatedNum;
});
}
console.log(
incrementDups(
[1, 2, 2, 3, 2, 2, 4] // [1, 2, 3, 4, 5, 6, 7]
)
);
console.log(
incrementDups(
[1, 1, 1, 1, 1, 1, 1] // [1, 2, 3, 4, 5, 6, 7]
)
);
console.log(
incrementDups(
[1, 99, 55, 4, 55, 2] // [1, 99, 55, 4, 56, 2]
)
);

Remove an item from an array by value, but leave the duplicates of that item

I have a very simple array like this:
array = [1, 1, 6, 7, 9, 6, 4, 5, 4];
I need to be able to remove a value, but I need to remove only one value if there's duplicate values. So if I remove the value 6, the array should become:
array = [1, 1, 7, 9, 6, 4, 5, 4];
The order of which one gets removed doesn't matter, so it could be the last no. 6 or the first no. 6. How can I do this?
Edit
I see there's a lot of confusion about why I need this, which results in incorrect answers. I'm making a Sudoku game and when a user inserts a number in a cell, the game has to check if the chosen number already occupies space in the same row or column. If so, the number of that specific row/column is added to this array. However, when a user fixes a mistake, the number of the row/column should be removed. A user can, however, make multiple mistakes in the same row or column, which is why I need to retain the duplicates in the array. Otherwise, users can make multiple mistakes in a row/column, and only fix one, and then the code will think there are no errors whatsoever anymore.
Hope this makes things more clear.
Try to get the index of your item with indexOf() and then call splice()
let array = [1, 1, 6, 7, 9, 6, 4, 5, 4];
let index = array.indexOf(6);
array.splice(index,1);
console.log(array);
var array=[1, 1, 6, 7, 9, 6, 4, 5, 4],
removeFirst=function(val,array){
array.splice(array.indexOf(val),1)
return array;
};
console.log(removeFirst(6,array));
You can use Array.prototype.findIndex to find the first index at which the element to be removed appears and then splice it.
Also you can create a hastable to ascertain that we remove only if a duplicate is availabe - see demo below:
var array = [1, 1, 6, 7, 9, 6, 4, 5, 4];
var hash = array.reduce(function(p,c){
p[c] = (p[c] || 0) + 1;
return p;
},{});
function remove(el) {
if(hash[el] < 2)
return;
array.splice(array.findIndex(function(e) {
return e == el;
}), 1);
}
remove(6);
remove(7);
console.log(array);
If order of removed element (not elements!) isn't important, you can use something like this:
array = [1, 1, 6, 7, 9, 6, 4, 5, 4];
function remove_if_dupe(elem, array) {
dupes=[];
for(i=0;i<array.length;i++) {
if(array[i] === elem) {
dupes.push(elem);
}
}
if(dupes.length>1) {
//is duplicated
array.splice(array.indexOf(elem), 1);
}
return array;
}
console.log(remove_if_dupe(6,array));
This should keep unique elements, hopefully.

How come I can't use the length number from this array?

I created an array, and when I try to get the length of the array it works fine.
var map = [
[3, 0, 0, 2],
[7, 6, 6, 8],
[7, 6, 6, 8],
[5, 1, 1, 4]
];
var i = map.length;
i outputs 4.
When I try to use the i variable to get the column using var j = map[i].length; the console returns "map[i] is undefined". How come this won't work, but replacing i with an actual number works?
Here is an example jsfiddle, just uncomment line 11.
i is equal to 4, as you said. JS array indices start from 0, so the last element in your array is map[3] which means there is no element at map[4]
You need to do map[i-1] - this code should work:
var j = map[i-1].length;
And here is it working in your jsfiddle: https://jsfiddle.net/zk7f8Ls2/2/
Because table index are zero-based. The table length is 4 but indexes are 0, 1, 2 and 3. When you try to access index 4, you will get an error.
It's because i is 4, and remember that arrays start with 0 if you want to see the last item of the array just add -1 map[i-1]

After creating a random javaScript-array with recurring integers - avoid identical integers side by side

have anybody any suggestion to "deal" a array after filling it randomly with recurring integers?
here is a easy example (3 x integer-group: 1,2,3,4):
array[1,2,3,4,4,2,3,1,1,4,3,2];
is there a way with a special function, to "rearrange" the array, by avoiding the same integers side by side.
this would be OK (no immediate neighbor is identical):
array[1,2,1,2,1,3,4,3,4,3,4,2];
those are not:
array[4,4,4,1,2,3,3,2,1,1,2,3];
array[1,2,3,2,1,3,3,4,4,1,2,4];
in my case the array could have 25 to 30 times the same integer-group.
hopefully I'm declaring comprehensible - so, you understanding my problem!
Thank you in advance for your efforts
FerO
Edited for clarity:
All integers must be conserved (not deleted) and the integers could be between 0 and 99!
changed "clean" to "rearrange"
Okay, now that I understand what you're after better - here you are. First, a caveat: there are arrays for which this is impossible. For instance, any array in which more than half the elements have the same value cannot be shuffled to keep those values away from each other.
That said:
var insert = function(val,arr) {
if (arr.length > 1) {
for (var i=1;i<arr.length;i++) {
if (arr[i-1] !=val && arr[i] != val) {
arr.splice(i,0,val);
return arr;
}
}
}
arr.splice(0,0,val);
return arr;
}
var shuffle = function(arr) {
return arr.reduce(function(p,c,i,a) {
return insert(c,p);
}, []);
}
shuffle() returns a new array built by shuffling the values of the input array; if you'd rather have them shuffled 'in place' by mutating the input array, that's easy enough to do. There may exist theoretically-shuffleable arrays which this algorithm fails for. I haven't found any in brief testing, but I also haven't proven they don't exist.
The algorithm here is:
Start with an empty 'destination' array.
For each element in the input array, traverse the destination array and insert it into the first position found where it does not equal either of its neighbors.
If no such position is found, stick it at the front.
(This would perform slightly better if it weren't ignoring the possibility of putting things in position 0 right off)
shuffle([4,4,4,3,3,3,2,2,2,1,1,1,1]) = [3, 1, 2, 1, 4, 1, 2, 1, 3, 2, 4, 3, 4]
shuffle([1,2,3,4,4,2,3,1,1,4,3,2,2]) = [2, 3, 2, 4, 2, 1, 3, 1, 4, 2, 3, 4, 1]
shuffle([1,1,1,1,1,1,1,2,2,2,2,2,2,2]) = [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
JSfiddle
You can use Array.prototype.filter() to remove values that are adjacent to identical values.
var myArray = [1, 2, 3, 4, 4, 2, 3, 1, 1, 4, 3, 2];
var deduped = myArray.filter(function (value, index, collection) {
return value !== collection[index+1];
});
// deduped is now [1, 2, 3, 4, 2, 3, 1, 4, 3, 2]

Categories