Related
I have an array of arrays of arrays and I'm trying to remove an element from a sub-sub array. The problem is the element is removed from all my sub-sub arrays. See code below:
let list = Array(9).fill(Array(9).fill([1,2,3,4,5,6,7,8,9]));
list[0][0].splice(3,1);
console.log(list[0][0],list[2][1],list)
Please let me know if you know how to solve that.
Array.prototype.fill fills the array with the exact same value that was provided to it. This is not a problem when filling the array with immutable values (like numbers, strings) but it's usually a problem with mutable values (like objects, arrays).
So, if you mutate one value in the array you would notice the change at all other places because all of them refer to the exact same value. Refer to example below for better understanding.
let nums = [1];
let arr = Array(2).fill(nums);
// mutating nums
nums[0] = 5;
// the change is visible in arr as well
console.log(arr[0]); // [ 5 ]
// and it's visible at both the indicies
console.log(arr[1]); // [ 5 ]
// mutating arr
arr[0][0] = 10;
// change is visible in nums as well
console.log(nums); // [ 10 ]
// and also at index 1
console.log(arr[1]); // [ 10 ]
You can use Array.from instead.
let list = Array.from({ length: 9 }, () =>
Array.from({ length: 9 }, () => [1, 2, 3, 4, 5, 6, 7, 8, 9])
);
list[0][0].splice(3, 1);
console.log(list[0][0]); // [ 1, 2, 3, 5, 6, 7, 8, 9 ]
console.log(list[1][0]); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
I'm new to Javascript and am currently going through the basics of callback functions and am having difficulty understanding why the code below returns an array of odds:
Code:
let numbers = [1, 2, 4, 7, 3, 5, 6];
function isOddNumber(number) {
return number % 2;
}
const oddNumbers = numbers.filter(isOddNumber);
console.log(oddNumbers); // [ 1, 7, 3, 5 ]
I see that the .filter is calling isOddNumber but I'm not sure how isOddNumber is passing or failing the values in the array. Is the return number % 2 pushing a 1 or 0 back to .filter and interpreting as truthy or falsy?
Any help here would be greatly appreciated!
#pilchard:
filter() passes each element to the provided isOddNumber callback, which in turn returns the remainder after dividing by 2 (either 0 or 1), which is evaluated as a boolean by the filter.
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.
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]
)
);
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]