Related
I am trying to remove certain values of a list doing a simple for loop. The loop seems to run 1 less time than expected. If I run children.length+1 it gives me an out of bounds error. Can someone see why this is occurring by simply looking at this code?
var children = document.getElementsByClassName("w-dyn-items")[0].children;
console.log(children.length) // 5
for (var i = 0; i < children.length; i++) {
var tableChild = children[i];
console.log(i, children[i]); // i = 0,1,2,3
id = tableChild.getElementsByClassName("text-block-3")[0];
console.log(id.textContent, msid)
if (id.textContent != msid) {
children[i].remove();
}
}
You're removing children, once you remove a child, children.length decreases., and when you check the next index, you will "skip" a row. You just need to decrement i when you remove:
if (id.textContent != msid){
children[i].remove();
i--;
}
E.g.
let arr = [0, 1, 2]
for (let i = 0; i < arr.length; i++) {
// i = 0; arr = [0, 1, 2]; arr[i] === 0;
// i = 1; arr = [1, 2]; arr[i] === 2; we skipped 1
if (arr[i] !== 2) arr[i].remove();
}
For some reason, the manipulated doubleArray below is not shown in the console. Any variables that I declare after the for loop won't show to the console on both cases. Consider that in the first algorithm, there is only one for loop with x being incremented everytime. Whereas, in the second algorithm, it's a nested for loop. Can someone help me fix my error in both algorithms?
First Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
var x = 0;
for (i = 0; i < helloWorld.length; i++) {
x = x + 1;
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
The second Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = 1; x < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
In first algorithm, you are only checking if the number at current index is equal to the number at the next index, meaning you are only comparing numbers at consecutive indexes. First algorithm will work only if you have duplicate numbers on consecutive indexes.
In second algorithm, you are incrementing i in both loops, increment x in nested loop, change x = 1 to x = i + 1 and your error will be fixed.
Here's the fixed second code snippet
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3, 1, 2];
var doubleValue = [];
for (let i = 0; i < helloWorld.length; i++) {
for (let x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate();
Heres's another way to find the duplicates in an array, using an object. Loop over the array, if current number is present as key in the object, push the current number in the doubleValue array otherwise add the current number as key-value pair in the object.
const isDuplicate = function() {
const helloWorld = [1,2,3,4,3, 1, 2];
const doubleValue = [];
const obj = {};
helloWorld.forEach(n => obj[n] ? doubleValue.push(n): obj[n] = n);
console.log(doubleValue);
};
isDuplicate();
Not entirely sure what you are trying to do. If you are only looking for a method to remove duplicates you can do the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_removed = Array.from(new Set(hello_world));
A set is a data object that only allows you to store unique values so, when converting an array to a set it will automatically remove all duplicate values. In the example above we are creating a set from hello_world and converting it back to an array.
If you are looking for a function that can identify all the duplicates in an array you can try the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_found = hello_world.filter((item, index) => hello_world.indexOf(item) != index);
The main problem by finding duplicates is to have nested loop to compare each element of the array with any other element exept the element at the same position.
By using the second algorithm, you can iterate from the known position to reduce the iteration count.
var isDuplicate = function(array) {
var doubleValue = [];
outer: for (var i = 0; i < array.length - 1; i++) { // add label,
// declare variable i
// no need to check last element
for (var j = i + 1; j < array.length; j++) { // start from i + 1,
// increment j
if (array[i] === array[j]) { // compare values, not indices
doubleValue.push(array[i]);
continue outer; // prevent looping
}
}
}
return doubleValue;
};
console.log(isDuplicate([1, 2, 3, 4, 3])); // [3]
You could take an object for storing seen values and use a single loop for getting duplicate values.
const
getDuplicates = array => {
const
seen = {}
duplicates = [];
for (let value of array) {
if (seen[value]) duplicates.push(value);
else seen[value] = true;
}
return duplicates;
};
console.log(getDuplicates([1, 2, 3, 4, 3])); // [3]
Your first algorithm doesn't work because it only looks for duplicates next to each other. You can fix it by first sorting the array, then finding the duplicates. You can also remove the x and replace it by ++i in the loop.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,6];
var doubleValue = [];
helloWorld = helloWorld.sort((a, b) => { return a - b });
for (i = 0; i < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[++i]) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
isDuplicate();
For the second algorithm loop, you probably meant x++ instead of i++ in the second loop. This would fix the problem.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,4];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x]) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate()
The first algorithm can't be fixed, it can only detect consecutive duplicates,
in the second algorithm you increment i in both loops.
To avoid the duplicates beeing listed too often, you should start the second loop with i + 1
I'm writing an function to create all possible permutation with max length limitation, and each item in array can only be used once.
The original permutation code as follows:
function permutations(list)
{
// Empty list has one permutation
if (list.length == 0)
return [[]];
let result = [];
for (let i=0; i<list.length; i++)
{
// Clone list (kind of)
let copy = Object.create(list);
// Cut one element from list
let head = copy.splice(i, 1);
// Permute rest of list
let rest = permutations(copy);
// Add head to each permutation of rest of list
for (let j=0; j<rest.length; j++)
{
let next = head.concat(rest[j]);
result.push(next);
}
}
return result;
}
How best to add this max length parameter to create unique combination result? I added maxLength. But in recursion stuck on where to best implement this parameter.
From a minimalist point of view, stop the recursion not when you have shrinked list from its size but when maxLength element have been taken from the list.
function permutations(list, maxLength)
{
// Empty list has one permutation
if (maxLength == 0)
return [[]];
let result = [];
for (let i=0; i<list.length; i++)
{
// Clone list (kind of)
let copy = Object.create(list);
// Cut one element from list
let head = copy.splice(i, 1);
// Permute rest of list
let rest = permutations(copy, maxLength-1);
// Add head to each permutation of rest of list
for (let j=0; j<rest.length; j++)
{
let next = head.concat(rest[j]);
result.push(next);
}
}
return result;
}
const maxLength = 4
console.time('by cp')
var r = permutations([1, 2, 3, 4, 5, 6], maxLength)
console.timeEnd('by cp')
console.log(r)
However, a similar approach but slightly more performant is to avoid copying the array for every "try" and only copy it when a solution is found
function permutations2(iList, maxLength)
{
const cur = Array(maxLength)
const results = []
function rec(list, depth = 0) {
if (depth == maxLength) {
return results.push(cur.slice(0))
}
for (let i = 0; i < list.length; ++i) {
cur[depth] = list.splice(i, 1)[0]
rec(list, depth + 1)
list.splice(i, 0, cur[depth])
}
}
rec(iList)
return results;
}
console.time('cp on solution')
var r = permutations2([1, 2, 3, 4, 5, 6], 4)
console.timeEnd('cp on solution')
console.log(r)
Starting with this initial 2D array:
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
I need to create this 3D array programmatically:
var fullArray = [
[[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]],
[[3,4],[0,1],[5,6],[2,3],[6,7],[3,4]],
[[4,5],[1,2],[6,7],[3,4],[0,1],[4,5]],
[[5,6],[2,3],[0,1],[4,5],[1,2],[5,6]],
[[6,7],[3,4],[1,2],[5,6],[2,3],[6,7]],
[[0,1],[4,5],[2,3],[6,7],[3,4],[0,1]],
[[1,2],[5,6],[3,4],[0,1],[4,5],[1,2]],
[[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]],
[[3,4],[0,1],[5,6],[2,3],[6,7],[3,4]],
[[4,5],[1,2],[6,7],[3,4],[0,1],[4,5]],
[[5,6],[2,3],[0,1],[4,5],[1,2],[5,6]]
];
See the pattern?
On each pair, the [0] position should increment to 6 (from any starting number <= 6) and then reset to 0 and then continue incrementing. Similarly, the [1] position should increment to 7 (from any starting number <= 7) and then reset to 1 and then continue incrementing.
In this example, there are 10 2D arrays contained in the fullArray. However, I need this number to be a variable. Something like this:
var numberOf2DArraysInFullArray = 12;
Furthermore, the initial array should be flexible so that initialArray values can be rearranged like this (but with the same iteration follow-through rules stated above):
var initialArray = [[6,7],[2,3],[5,6],[4,5],[1,2],[6,7]];
Any thoughts on how to programmatically create this structure?
Stumped on how to gracefully pull this off.
Feedback greatly appreciated!
Here's a solution, I've separated the methods, and I made it so if instead of pairs it's an N size array and you want the [2] to increase up to 8 and reset to 2, if that's not needed you can simplify the of the loop for(var j = 0; j < innerArray.length; j++)
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
var create3DArray = function(array, size){
var newArray = [initialArray];
for(var i = 0; i < size; i++)
{
newArray.push(getNextArrayRow(newArray[i]));
}
return newArray;
}
var getNextArrayRow = function(array){
var nextRow = [];
for(var i = 0; i < array.length; i++)
{
var innerArray = array[i];
var nextElement = [];
for(var j = 0; j < innerArray.length; j++)
{
var value = (innerArray[j] + 1) % (7 + j);
value = value === 0 ? j : value;
nextElement.push(value);
}
nextRow.push(nextElement);
}
return nextRow;
}
console.log(create3DArray(initialArray,3));
Note, the results from running the snippet are a bit difficult to read...
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
var numOfArrays = 10;
// get a range array [0, 1, 2, ...]
var range = [];
for (var i = 0; i < numOfArrays; i++) {
range.push(i);
}
var result = range.reduce(function(prev, index) {
if (index == 0) {
return prev;
}
prev.push(transformArray(prev[index - 1]));
return prev;
}, [initialArray])
console.log(result);
function transformArray(arr) {
return arr.map(transformSubArray)
}
function transformSubArray(arr) {
return arr.map(function(val) {
return val == 7 ? 0 : val + 1;
})
}
Here's a pretty simple functional-ish implementation
Say you have an array like this and I want to reorder it.
var myarray = [
[178, 559, 906, 1252] , [381 , 537 , 937 , 1115] , [346 , 529 , 913 , 1069]
];
What I wanted to do was to loop through each of the arrays, take the 1st value of each array on the first loop and push each into a separate array. The second time the loop runs, take the 2nd value of each array, push those into the separate array and so on and so on. So that the separate array looks like this;
var myNewArray = [178, 381, 346, 559, 537, 529, 906, 937, 913, 1252, 1115, 1069];
I've got so far as to loop through each array and get all of the values, but can't work out the logic to target ONLY the 1st values on the first loop, the 2nd of the second loop etc etc
var arraylength = myarray.length;
for (var i = 0; i < arraylength; i++ ) {
console.log(i+1 + " time around");
var noc = myarray[i].length;
for (var k = 0; k < noc; k++) {
var a = myarray[i][k];
console.log(a);
};
};
Here's a JSFiddle
Okay...
Perhaps this is overboard, but I had a long trip home and some time to kill.
Your algorithm troubles revolve around the fact that you're missing a step.
What you actually need to do is loop through the range of the longest array.
Which means that you need to create a range (an actual range, or just know the min/max bounds of it) that goes from 0 to the max of all of the lengths of all of the arrays.
When you've done that, you need to go through that range, and within that range, you need to go through the list of all arrays (looping through each 2nd-dimension array, per iteration).
For each array, you check if it has an element at the current index.
If it does, add it to the new array.
The first step (the one you're missing) is almost like dealing cards; you have 4 people at the table, but it's actually the 52 cards that you're iterating through on the outside, not the 4 people.
This has a bunch of different names, depending on what you're doing.
This might be a zip a merge a rotation (though rotation doesn't really account for the flattening, just the shuffling).
So without further ado, here are 3 solutions, which are all different takes on this.
The first solution is the more-classical "JavaScript as Java" implementation:
function findMax (arrays) {
var i = 0;
var l = arrays.length;
var max = 0;
var array = [];
for (; i < l; i += 1) {
array = arrays[i];
max = array.length > max ? array.length : max;
}
return max;
}
function rotateAndFlatten (arrays) {
var flattenedArray = [];
var maxLength = findMax(arrays);
var inner = 0;
var outer = 0;
var array;
var currentValue;
for (; outer < maxLength; outer += 1) {
for (inner = 0; inner < arrays.length; inner += 1) {
array = arrays[inner];
currentValue = array[outer];
if (currentValue || currentValue === 0) {
flattenedArray.push(currentValue);
}
}
}
return flattenedArray;
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES3").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES3").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES3"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES3"></output></code></pre></div>
The second is the ES5 way I'm more used to thinking, these days, with partially-applied functions, and working with sets of things one operation at a time, rather than instances of things with manual loop management:
function makeRange (min, max) {
var range = [];
var i = min;
while (i < max) {
range.push(i);
i += 1;
}
return range;
}
function concat (a, b) {
return a.concat(b);
}
function identity (x) {
return x;
}
function max (a, b) {
return b > a ? b : a;
}
function pluck (key) {
return function pluckFrom (obj) {
return obj[key];
};
}
function fillIndexArrays (arrays) {
return function (i) {
return arrays.map(pluck(i));
};
}
function rotateAndFlatten (array) {
var getLength = pluck("length");
var maxLength = array.map(getLength).reduce(max, 0);
var indices = makeRange(0, maxLength);
return indices.map(fillIndexArrays(array)).reduce(concat, []).filter(identity);
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES5").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES5").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES5"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES5"></output></code></pre></div>
And here's the ES6 version of that, which can now use generators and splat operators to greatly simplify the construction of a range, and use lambdas to where code might be compacted and be equally legible (to me/my team):
const max = (a, b) => b > a ? b : a;
const identity = x => x;
const concat = (a, b) => a.concat(b);
function * range (min, max) {
let i = min;
while (i <= max) {
yield i;
i += 1;
}
};
const pluck = (key) => { return (obj) => obj[key]; };
function rotateAndFlatten (arrays) {
const getLength = pluck("length");
const maxLength = arrays.map(getLength).reduce(max, 0);
const indices = [...range(0, maxLength)];
return indices
.map(i => arrays.map(pluck(i)))
.reduce(concat, [])
.filter(identity);
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES6").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES6").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES6"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES6"></output></code></pre></div>
As a bonus, here's how I might implement it if I were writing JS as though I were writing C code that makes me very sad when I have to debug a logic error (but cuts right to the quick of the algorithm):
function init (arrs) {
var max;
var i = 0;
var l = arrs.length;
var max = 0;
for (i = 0; i < l; i++)
if (max < arrs[i].length)
max = arrs[i].length;
var j = 0;
var arr = [];
for (i = 0; i < max; i++)
for(j = 0; j < arrs.length; j++)
if (arrs[j][i] !== undefined)
arr.push(arrs[j][i]);
document.querySelector(".ResultOutput--C").value = JSON.stringify(arr);
}
var arrs = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
document.querySelector(".ResultInput--C").textContent = JSON.stringify(arrs);
init(arrs);
<div ><pre>Input: <code class="ResultInput ResultInput--C"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--C"></output></code></pre></div>
Hope that gives you something to chew upon, and some insight into the low-level algorithms which might be at play, and the higher-level ways of implementing those lower-level algorithms.
Alternatively you can do it like this: .shift() returns the first item of the array, but note that this solution will alter the original array
var myarray = [
[178, 559, 906, 1252],
[381, 537, 937, 1115],
[346, 529, 913, 1069]
];
var resultArray = [];
var maxlen = Math.max.apply(null, myarray.map(function(i) {
return i.length; }));
var l = 0;
while (l < maxlen) {
var s = 0;
var a = [];
for (i = 0; i < myarray.length; i++) {
a.push(myarray[i].shift());
}
resultArray.push(a);
l++;
s++;
}
console.log(resultArray);
Instead of iterating myarray in the outer loop, and each subarray in the inner one, you should do the opposite. Then it's a bit tricky, because the outer loop iterates the subarrays, but they could have different lengths. I added a done variable to know where to stop.
var myNewArray = [];
for(var i=0, done=false; !done; ++i) {
done = true;
for(var j=0; j<myarray.length; ++j) {
if(i < myarray[j].length) {
done = false;
myNewArray.push(myarray[j][i]);
}
}
}
Lodash has a method called zip that does what you are trying to do.
console.log(_.zip(myarray)); // [[178, 381, 346], [559, 537, 529], [906, 937, 913], [1252, 1115, 1069]]