Creating arrays on condition in javascript - javascript

This has been eating me away. Check CodePen here. I have a function that adds elements to a ruler. Call expose.init(lengthOfPeriod) with an integer to set the length of each period in the ruler. Call expose.addAction(time, team) with an integer and a string == 'HOME' || 'AWAY' to place an action in the ruler.
When the screen is resized, I want something to happen to the elements in the ruler that touch each other (basically collapse into a group).
I have a function 'detectOverlap' that takes 2 params and determines if they are touching or not. I also have a function in the resize event handler that populates an array 'allTouching' with all the elements in the ruler that are touching each other at each resize.
if (actionArray.length > 1) { //don't run if there's only one element in the page
actionArray.reduce(function (prev, next) {
if (detectOverlap(prev, next)) {
if (allTouching.indexOf(prev, next) === -1) allTouching.push(prev, next);
}
return next;
});
If actions are touching each other, I need them to collapse into groups. In order to do this, I need to create an array for each group of actions touching each other. However, I haven't been able to make this happen so far.
Here's some pseudo code:
for (i = 0; i < allTouching.length; i++) {
if (detectOverlap(allTouching[0], alltouching) {
touchingGroup[i] = new Array(allTouching[0], all other elements touched by 0);
do not include any element more than once in touchingGroup[i];
do not include any similar arrays (same elements) in allGroups;
allGroups.push(touchingGroup[i]);
}
}
In short, this would need to loop for all the elements in the allTouching array, and create a new touchingGroup[n] for each new group of actions that touch each other.
This sounds simple in my head, and I'm sure there must be a way to do it without code getting overly complex, but I haven't found it yet.
Any feedback would be appreciated.

It seems your question is only about the grouping, so I will ignore the visualisation aspect and assume that the function detectOverlap is correct.
Then you could create the groups in one for loop. In this snippet I have added simplistic sample data and a mock detectOverlap function that will return true when its two arguments are the same (just for the purpose of the snippet):
// Simplistic mock data and function, just to make the snippet work
var actionArray = [1, 1, 3, 3, 3, 8, 9];
function detectOverlap(a, b) { return a === b; }
// Actual code:
var allGroups = [];
var start = 0;
for (var i = 1; i <= actionArray.length; i++) {
if (i >= actionArray.length || !detectOverlap(actionArray[i-1], actionArray[i])) {
if (i - start > 1) // Remove this condition if you want singletons as well
allGroups.push(actionArray.slice(start, i));
start = i;
}
}
console.log(JSON.stringify(allGroups));
Explanation
The variable start is used as an index in the array, from where the most recently found group should start. That group is not yet actually created, since we do not know where it ends, and so I will call this the "undecided" group. start is initialised at 0 to indicate that the first group will start there.
The loop iterates over the array, but starting at 1. In each iteration it decides whether the "undecided" group (started at start) is complete. The group is considered complete when there is no overlap between the previous and the current element of the array. In that case the previous element is the last element of the "undecided" group. The elements for that group are copied from the array with slice. Note that the second argument of slice is the index of the first element that should not be part of the group. Now that group is stored, and start is put at the current index, where the next (and only) "undecided" group should start.
But as long as the two elements do overlap, no new group should be created (that is why the condition has a !). Instead start remains unchanged, and so this "undecided", "unclosed" group is getting bigger in size.
There is an if just before that slice, which prevents the creation of groups that only contain one element. If you remove that if, then also single elements will be isolated in their own "singleton" groups.
The loop will go up to and including arrayAction.length: this is unusual, since that makes the last i an invalid index. But it is useful, since in that case we still want to finish up the last group that is still "ongoing". So in that case i >= arrayAction.length will be true, and so the detectOverlap function will not be called (because the if condition is already known to be true). The if block will be entered and the final group will be created.

Related

arr.splice() is timing out in loop - better way to replace arr[i]?

So I am having an issue with my solution, and I may be entirely off on what needs to be done. I keep timing out which makes me believe I have the .splice() in an incorrect location.
The problem:
You are given an array of integers. On each move you are allowed to increase exactly one of its element by one. Find the minimal number of moves required to obtain a strictly increasing sequence from the input.
Example
For inputArray = [1, 1, 1], the output should be
arrayChange(inputArray) = 3.
My Pseudo code
First check and see if the current index is greater than the next index. If not, continue check through the entire loop. If so, add one to the next index and test again until true. If you increment the next index by one, add one to a variable "moves". Return moves
function arrayChange(inputArray) {
for( var i = 0; i < inputArray.length; i++){
var addition = (inputArray[i+1]+1)
if(inputArray[i] >= inputArray[i+1]){
inputArray.splice(i,0, addition);
}
}
return inputArray;
}
My Error:
Execution time limit exceeded on test 1: Program exceeded the execution time limit. Make sure that it completes execution in a few seconds for any possible input.
Why your code fails:
for( var i = 0; i < inputArray.length; i++){//iterate until reaching end of array
var addition =(inputArray[i+1]+1);
if(inputArray[i] >= inputArray[i+1]){ //if its not increasing
inputArray.splice(i,0, addition);
}
}
Lets examine a sample input:
arrayChange([1,1]);
So inputArray[i] is 1, and inputArray[i+1] is 1.
Therefore it will go into the if, as 1>=1. So it will add 1+1 into the array,but not at the end but at i, so at position 0. It will look like this:
[2,1,1]
The loop will go on:
[2,2,2....,1,1]
It will never end.
To make it work, you must stop at array.length-1 :
for( var i = 0; i < inputArray.length-1; i++){
and you must insert at the right index,and also remove one:
inputArray.splice(i+1,1,addition);
Non array changing approach:
function numchanges(array){
var before=array[0],counter=0;
for(var i=1;i<array.length;i++){
if(before>=array[i]){
//we need to change array[i] to before+1, so:
counter+=(++before)-array[i];
}else{
before=array[i];
}
}
return counter;
}
console.log(numchanges([1,1,1]));//should return 3
How it works:
A proper strictly monotonically increasing function would have values like this:
[1,2,3,4,5,10]
So it might at least go one up, or it jumps up. So lets take one random array, and its valid counterpart:
[1,1,3,4,5,-1]
[1,2,3,4,5,6]
So the changes needed:
[0,1,0,0,0,7] => 8
So the upper code keeps the valid number ( before ) and the needed change while iterating from left to right. current starts with the first array item:
before=array[0];//1
But we dont need to change the first array element, so we start at i=1.If this number is valid, so
before<array[i]
we just go on:
before=array[i];
i++;//due the for loop
If not, its not valid and we need a correction.
result+=before+1-array[i];
So if the last was 5 (=before) and now weve got -10 (=array[i]), we need to correct it to 6 (=before+1);
result+=6--10//==16
So we need another 16 corrections...

onkeydown, and auto complete

I was wondering if anyone could help me solve this issue or point me towards the right direction.
In my project we have a filed that needs to be autofilled, at this moment I use onblur which works wonders as it only does it so once you leave the focus. However, due to recent changes, it needs to only do so when there is only one unique item in the map which it matches the input.
I have a large array defined as following:
var myArray = [
[content, content],
[content, content],
...
]
Later in my code I associate it with a map, at least this is what most stackoverflow questions I looked at referred to it as follows:
var myMap = {};
for(0 to myArray.length) {
var a = myArray[i][0];
var b = myArray[i][1];
myMap[a] = b;
}
Now, finally I iterate over this array as follows:
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0)
the above is the line of code I am struggling to figure out how to change. At this moment, while using on blur, if I type in the letter 'A' for example, and leave the focus area it will automatically fill it in with a certain name. However, in the array there are many other objects that begin with, or contain A. How can I change it so that the onkeydown event will keep going until it finally filters it down to to only possible key-value pair? I tried looking at MDN's documentation for filtering, but I do not think that will work for my purposes, or at least I am too inexperienced with JS.
If the indexOf the first and last are nonnegative and equal, there is just one. You could do this with an && and boolean short circuit evaluation, but that will run very far right off the screen, so I am showing your code with one more nested if (up to you to add the end of the block). But we also need to see if there are matches on multiple keys.
var matchCount=0;
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0){
if (map[key].toLowerCase().indexOf(location.toLowerCase()) == map[key].toLowerCase().lastIndexOf(location.toLowerCase())) {
matchCount++;
then outside your for loop:
if (matchCount==1){ //do your stuff

Looping to a specific index within a string

I'm currently doing a puzzle and am having trouble formulating my thoughts on how to do the following. Heres some code for some context on my thoughts (this isn't working yet)
const words = ['this', 'is', 'my', 'dog', 'bit', 'bite', 'every'];
const strings = ['thisis', 'thisisacat', 'thisisaduck', 'thisismy', 'thisismydog', 'bitevery'];
var count;
for (var i = 0 ; i < strings.length ; i++)
{
for (var j = 0; j <words.length; j++)
{
if(strings[i].indexOf(words[j]) !== -1)
{
count+=words[j].length;
}
}
}
Basically I want to first go to the word this check if it is in strings[j]. If it is then get the length of words[i], and go to the words[i]'th position inside strings[j] meaning I want the 'is' part of the 'thisis'. Basically I just want to test whether each element in strings can be represented by any amount of combos in words. This of course can be done with regular expressions, but I would like to use the current thought process that I have.
TLDR
With my current way, how can I check whether words in the words array exist within each element of strings(I am attempting this with indexOf), and if it is, go to the length of that words position meaning go to the position where i is in thisis. I realize that the way I am phrasing it it might be a bit verbose, but I'm not sure how else to phrase it. Any help is appreciated.
Example Run
We are at strings[i], now we check if any element in words is in strings[i]. Now this is in 'thisis', so add an index of four, since this has a length of four, to get to the 4th position and beyond of thisis, now we have isso loop again through the rest of words, now is is inside is, that means we have succesfully created a word from using some amount of words in the words array. Note, I actually don't want to delete the this from thisis, as I know modifying strings altogether is very costly in memory.
Another Run
Take thisismydog in strings. Let us iterate through words. this is inside thisismydog, so now go to the 4th onwards of thisismydog ( since this has a length of 4) namely now we consider ismydog. Now iterate again looking at the elements in words. We see is is there, so we go to the second element in ismydog, namely mydog. Since we reach the end of the word we can successfully conclude that thisismydog can indeed be represented by a combination of those elements within words.

Wait before start new loop at FOR - JS

I'll let you see first what i got:
CODE
I decided to experiment and make a script which order numbers in an array in every possible way.
The first way I'm doing is the "Selection mode", where you find the lower value of the array and you put it at the first position. As you can see I printed it into a table to make it funnier, but here comes my question, i want to put a delay at the end of every loop, where i highlight the values that I'm going to exchange, but for some reason it just organize all the table and then render it instead of going step by step.
Now I'm going to proceed to explain a bit my code ^^u:
If you click at "Restart" you'll get a new bunch of random numbers, and if you click at "Selection" the array will become ordered.
Functions:
generate: generate i numbers with a value from 0 to j.
prinTable: print the table.
renderDots: adds styles at the dots which match with the array values.
seleccion: execute the "Selection" way to organize the array.
highlight: adds styles at the dots which will be exchanged at THIS loop, and remove highlight styles of the others points.
I would like to get an affect like this:
You can use the setInterval function instead of a for loop:
function seleccion(table){
var ordenados = 0;
var timer = setInterval(renderOneStep, 300)
function renderOneStep(){
if (ordenados < table.length){
// do all the calculations
ordenados++;
} else {
clearInterval(timer);
}
}
}
The renderOneStep function updates one column. It is called every 300ms.
Please see the updated jsfiddle.

How can I remove blank lines in an array generated that show in a dropdown

I have a dropdown that is generated using javascript and html. I have some code which I will post below that loops through this list and should potentially remove any blank lines found but is not working. "$maxfield1rows" has a value of 7, what I am saying is that if the value is a blank (=='') then remove it. I used removeChild but this doesn't seem to work, I also tried splice, I think filter can work but am not sure. I tried the disabled=true but that just makes them disabled and unselectable. Can someone please help?
for(index=1; index<$maxfield1rows; index++) {
if(document.pickDivision.field1.options[index].value=='') {
document.pickDivision.field1.removeChild(document.pickDivision.field1.options[index]);
}
}
Updated below, I'm using $maxfield1rows since that is the amount of maximum number of rows the the loop goes through, also by change I meant that there is an onchange event that gets triggered when the user selects a different option in the dropdown menu, so depending on the option selected the output for field1 changes, sometimes there are 5 values that show and sometimes just 1:
for(index=$maxfield1rows-1; index>=0; index--) {
alert(document.pickDivision.field1.options[index]);
if(document.pickDivision.field1.options[index].value==''){
document.pickDivision.field1.removeChild(document.pickDivision.field1.options[index]);
document.pickDivision.field1.options[index].disabled=true;
}
else{
document.pickDivision.field1.options[index].disabled=false;
}
}
To expand upon my comment, I think the issue is that you are looping forwards through the options array. I am surprised that no error is being thrown when you try to do this. You should loop backwards through the collection to keep from skipping an item.
JS Fiddle demonstrating the error. In the example, items 1 and 2 are blank and item 3 is not.
These built in collections are changed each time your add/remove an item. Using the example in the fiddle, my array changes from [1,2,3] to [2,3] to error no item at index 2.
Looping backwards, my collection takes this change: [1,2,3] to [1,3] to [3].
Here is the code with comments explaining what each part of the for loop is used for and why. You can use a while loop if you prefer too.
//options.length - 1 because arrays are 0 based
//i >= 0 because you don't want to use a negative index on an array
//i-- to loop backwards
for(var i = document.pickDivision.field1.options.length - 1; i >= 0; i--) {
//Is this an empty item
if(document.pickDivision.field1.options[i].value == '') {
//Remove the empty item
document.pickDivision.field1.removeChild(document.pickDivision.field1.options[i]);
}
}
UPDATE
With the newly updated code you posted, you are attempting to access an option element after it has been removed. From the looks of the line, you don't need it anymore as you are already removing the element. If you do still want to disable the element before removing it, move that line above the other line (see comment in code).
for(index=$maxfield1rows-1; index>=0; index--) {
alert(document.pickDivision.field1.options[index]);
if(document.pickDivision.field1.options[index].value==''){
document.pickDivision.field1.removeChild(document.pickDivision.field1.options[index]);
//This line is causing the issue; move it above the previous line or remove it
document.pickDivision.field1.options[index].disabled=true;
}
else{
document.pickDivision.field1.options[index].disabled=false;
}
}
UPDATE 2
Per the question in the comments, when you change the number of options to 6, your code breaks. This is because you are using the hard coded PHP value $maxfield1rows. Since you already know the name of the form and the name of the field in the form, I would recommend you use the length of the options collection in your for loop rather than this variable. This will make sure that no matter how many option elements there are (1, 10, 1000), you will always loop through the entire collection.
for(var i = document.pickDivision.field1.options.length - 1; i >= 0; i--) {

Categories

Resources