Task: I have var = array; in a function that can hold the following sample array - the items represent time (seconds):
["20.30", "30.55", "8.25", "32.74", "2.75", "39.24"]
A setInterval loop displays the third item (array[2]) when a timer exceeds or matches second item in array (array[1]). Currently, after that happens, the first two items of array (array[0] and array[1]) are removed and array variable is re-indexed like so, looping the process until done:
array = array.splice(2).filter(function(){return true;});
Dilemma: I thought it would be easiest to just remove the first two items and update the array this way so as to simply apply a condition like so: if ( array[1] <= timer ){ but the splice() and filter() methods - which apply after the conditional statement in the loop - don't seem to re-index array variable.
Is there a way to make sure the array variable is re-indexed by new length, or perhaps there is a better way by navigating through the array variable without having to mutate/update it? The latter would perhaps be best, but I am kind of noob in better array manipulation and would appreciate some help.
UPDATE
Below I summarize what I clearly wish to attain:
var array = ["20.30", "30.55", "8.25", "32.74", "2.75", "39.24", etc...]
Instead of programmatically fetching each and every item iteration (impossible since amount will dynamically change) like follows ...
timelinePolling = setInterval(function(e){
...
if ( array[1] <= timer ){
timeline(array[2]); // Apply 3rd item timeline value
} else if ( array[3] <= timer ){
timeline(array[4]); // Apply 5th item timeline value
}
// etc, for remaining determined/undetermined items.
}, 30);
... I wish to iterate and dynamically increment the index target of array item variable (what is in brackets) the moment array[index] <= timer is achieved, like so (pseudo):
timelinePolling = setInterval(function(e){
...
if ( array[end] <= timer ){
// [end] is always an odd-numbered 0-based indexed item
// in array list (see Note1 below).
timeline(array[start]);
// [start] is always an even-numbered 0-based indexed item
// in array list (see Note2 below).
// Note1: [end] must be swapped with next chronological
// odd-numbered indexed item in array list at this point in code.
/* Missing code here! */
// Note2: [start] must be swapped with next chronological
// even-numbered indexed item in array list at this point in code.
/* Missing code here! */
}
}}, 30);
So it's the dynamic index increment part that I wish to resolve, which would make my day to say the least. Perhaps my array should be constructed differently to facilitate the task? In this fashion the array must remain intact (no mutation). Any pointers/help will be appreciated.
Looks like you code in is in the right direction take a look at circular arrays:
https://www.geeksforgeeks.org/circular-array/
Possibly you are looking at something like this -
index = beg;
while (index < end) {
if( arr[index] < timer) {
// set beg and end appropriately
// swap the values you need from the notes
// Note1: [end] must be swapped with next chronological
// odd-numbered indexed item in array list at this point in code.
/* Missing code here! */
// Note2: [start] must be swapped with next chronological
// even-numbered indexed item in array list at this point in code.
/* Missing code here! */
timeline(arr[index+1]);
}
index += 2; // jump to the next even number.
index %= arr_len; //check here if there is something that needed to be done for even odd index
}
You could do this with setTimeout, and then you won't have to do the comparison yourself.
At the end of each timeout loop, you splice the array as you suggested, if there are still 3 or more entries in the array then recall the function that sets the timeout.
(The timer here is only to display the total time)
(I reduced your array of number to speed it up)
let timer = 0;
const times = ["4.30", "5.55", "6.25", "6.74", "5.75", "4.24"];
function test(array) {
const timeInMilliseconds = parseFloat(times[1]) * 1000;
setTimeout(() => {
document.querySelector('#ignore').innerHTML = array[0];
document.querySelector('#test').innerHTML = array[1];
document.querySelector('#display').innerHTML = array[2];
timer += timeInMilliseconds / 1000;
document.querySelector('#timer').innerHTML = timer;
document.querySelector('#array').innerHTML = array;
array = array.splice(2);
if (array.length >= 3) {
test(array);
}
}, timeInMilliseconds);
}
test(times);
<p>ignore: <span id="ignore">-</span></p>
<p>test: <span id="test">-</span></p>
<p>display: <span id="display">-</span></p>
<p>Timer: <span id="timer"></span></p>
<p>Array: <span id="array"></span></p>
Related
I'm working on a leet code problem with the following instructions:
You are given an array prices where prices[i] is the price of a given stock on the ith day.
You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.
Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.
In my code below, I expect to remove the Max number from the array only if it's in index 0 of the array. When checking the debugger, I see that the entire array is deleted except index 0. Why is splice not working as intended?
what debugger shows
var maxProfit = function (prices) {
let theMin = Math.min(...prices)
let minPosition = prices.indexOf(theMin)
let theMax = Math.max(...prices)
let maxPosition = prices.lastIndexOf(theMax)
if (maxPosition === 0) {
prices = prices.splice(0, 1)
if (prices.length === 0) {
return 0
}
maxProfit(prices)
}
return theMax - theMin
};
splice does not return a modified copy of the array. The array is modified in-place, and what is returned is the deleted subarray, if any.
In effect, your code is:
const deletedElements = prices.splice(0, 1);
prices = deletedElements;
You deleted the first element from the array, then replaced that array with another array which only contains the first element. Thus, it is not that only the first element is returned, as you claim. splice is working exactly as documented.
Also, prices.splice(0, 1) is more legibly written as prices.shift().
function question1(){
if(sessionStorage.quizCounter == 0){
var qa0 = qs[Math.floor(Math.random() * 19)];
document.getElementById('questions_container').innerHTML = qa0[0]
for (let i = 1; i <= 3; i++) {
document.getElementById('answers_container').innerHTML += qa0[Math.floor((Math.random() * 2))+1];
}
}
}
That's my function, it's supposed to place the first item in the array in a <div id="questions_container"> which works, but the second part - the for loop - doesn't work.
The for loop is supposed to paste the last 3 items randomly in <div id="answers_container">. And I don't know how to do that beyond the idea. As it stands now it prints duplicates, which I don't want.
qa0[0] is always the question. qa0[1, 2, 3] is always the answers. qa0[] contains always 4 items. I need the first item to always be on qa0[0].
I figured to answer this question in the easiest possible way.
If you dont need to worry about the positions, you can just randomize the data and then remove the N items off the end.
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
This will shuffle your array, essentially randomizing it. You can then pop the items off the end
function randomitems(arr, returnedCount){
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
const clonedData = arr.slice()
shuffle(clonedData)
if ( returnedCount === undefined) return clonedData;
return clonedData.slice(-1 * returnedCount)
}
This will take up some space because I dont want to change the original object.
Smooth, and Quick. No super complex code.
It will return a random list, and it will return only duplicates IF there are duplicates in the base arr. Otherwise, it will maintain uniqueness of array items.
This will return an array of items, you can then loop that list or do whatever you specifically need with the desired results.
If you want to get super Computer Science-y, I can also give a hyper optimized random set, but that wouldn't likely see much in terms of returns until your dataset started to get big.
This would be used in the following case for you:
// This will link all the questions and possible answers together.
qa0 = [question, ans1, ans2, ans3, ans4].
// The next step is that you need to shuffle the answers.
tmpVar = [].concat(qa0[0], randomizeItems(qa0.slice(1))); // no number will just return the shuffled set.
qa0 = tmpVar; // You could likely just not even use this tmpVar and just do assignment if you really wanted to.
That way, qa0, will have the question be the 0 index, and all items after are shuffled. So it would then look like: qa0 = [ question, ans3, ans1, ans2, ans4];
I am trying to remove an item from an Array using Splice method.
arrayFinalChartData =[{"id":"rootDiv","Project":"My Project","parentid":"origin"},{"1":"2","id":"e21c586d-654f-4308-8636-103e19c4d0bb","parentid":"rootDiv"},{"3":"4","id":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02","parentid":"e21c586d-654f-4308-8636-103e19c4d0bb"},{"5":"6","id":"b8d2598a-2384-407a-e2c2-8ae56c3e47a2","parentid":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02"}];
ajax_delete_id = "e21c586d-654f-4308-8636-103e19c4d0bb,deca843f-9a72-46d8-aa85-f5c3c1a1cd02,b8d2598a-2384-407a-e2c2-8ae56c3e47a2";
$.each(arrayFinalChartData, function (idx, obj) {
var myObj = obj.id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
var vararrayFinalChartDataOne = arrayFinalChartData.splice(idx, 1);
}
});
console.log(arrayFinalChartData);
Please check at : http://jsbin.com/deqix/3/edit
Note : It does not complete the "last leg " of the loop. That means if I have 4 items, then it successfully executes 3 items. Same goes for 6,7...items.
I need to "REMOVE" few items and "PRESERVE THE BALANCE" in an array.
You can use for loop instead of $.each function:
alert('length before delete ' + arrayFinalChartData.length);
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
id = arrayFinalChartData[i].id;
if(ajax_delete_id.indexOf(id) > -1){
arrayFinalChartData.splice(i, 1);
}
};
alert('length after delete ' + arrayFinalChartData.length);
Demo.
Complete edit :
After researching a bit, and console.logging a lot, I finally found where the issue is coming from ! It's actually quite simple, but very sneaky !
Theoretical explanation :
You are calling the splice function with the variable "idx", but remember that the splice function remaps / reindexes your array ! So, each time you splice the array, its size decreases by one while you're still inside the $.each function. The splice messes up jQuery indexation of your array, because jQuery doesn't know that you're removing elements from it !
Iterative explanation :
$.each function starts, thinking your array has 4 elements, which is true, but only for a while. First loop, idx = 0, no splice. Second loop, idx = 1, splice, which means that your array has now 3 elements left in it, reindexed from 0 to 2. Third loop, idx = 2, splice, which means your array has now two elements left in it, but $.each continues ! Fourth loop, idx = 3, js crashes, because "arrayFinalChartData[3]" is undefined, since it was moved back each time the array got spliced.
To solve your problem, you need to use a for loop and to start analyzing the array from the end, not from the beginning, hence each time you splice it, your index will decrease as well. And if you want to preserve balance, just push the removed items into an array. Remember that you are analyzing the array from the end, so items pushed into the "removedItems" array will be in reverse order. Just like this :
var removedItems = new Array();
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
removedItems.push(arrayFinalChartData.splice(i, 1)[0]);
}
}
console.log(arrayFinalChartData);
console.log(removedItems);
And a working demo (inspect the page, observe the console and click "Run") :
http://jsfiddle.net/3mL6C/3/
I will not give credit to myself for this answer, thanks to another similar thread for giving me a hint.
Your problem here is that when the $.each is set up, it's expecting a certain length of object, which you are then changing. You need to loop in a way that respects the dynamic length of the object.
var i = 0;
while (i < arrayFinalChartData.length) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
// current item is in the list, so remove it but KEEP THE SAME INDEX
arrayFinalChartData.splice(i, 1);
} else {
// item NOT in list, so MOVE TO NEXT INDEX
i++
}
}
console.log(arrayFinalChartData);
Demo
there are many questions/answers dealing with this topic. None match my specific case. Hopefully someone can help:
I have an array of indexes such as:
var indexes = [24, 48, 32, 7, 11];
And an array of objects that look similar to this:
var items = [{
name : "whatever",
selected : false,
loading : true,
progress : 55,
complete : false
},
{
name : "whatever 2",
selected : false,
loading : false,
progress : 100,
complete : true
}];
Each integer within the indexes array corresponds to the actual index of an object within the items array.
Lastly I have a variable which defines the new insert position within the items array:
var insertindex = ??
What I would like to do is to take all objects in the items array that have the indexes stored in the indexes array, remove them, then finally place them back, all next to each other at a specified index defined by the variable insertindex.
I have been trying to use splice() by copying the objects at each index to a temporary array, then removing them from the original array, then finally looping through this new temporary array and putting them back into the original items array at the new positions, but seems to be hitting a mental brick wall and cannot get it to work correctly.
To summarize, I simply want to take all objects from the items array that match an index defined in the indexes array, put them together and reinsert them at a predefined index, back into the items array.
To help with conceptual visualization. If you think of the app as a javascript file manager, allowing the reordering of multiple file selections which do not have to be adjacent. The indexes array defining the current selection and the items array defining the list of files. And finally the rearoderindex defines the new insert position that all selected files should move to.
EDIT: As was rightly suggested here is the code I am playing with right now:
function reorder(items, indexes, insertindex){
var offset = 0;
var itemscopy = items.slice(0); //make shallow copy of original array
var temparray = new Array(); // create temporary array to hold pulled out objects
//loop through selected indexes and copy each into temp array
for(var i=0, len=indexes.length; i<len; i++){
array[i] = itemscopy[self.cache.selecteditems[i]];
}
//remove all selected items from items array
for(var i=0, len=indexes.length; i<len; i++){
items.splice(indexes[i], 1);
}
//finally loop through new temp array and insert the items back into the items array at the specified index, increasing the index each iteration using the offset variable.
for(var i=0, len=temparray.length; i<len; i++){
items.splice((insertindex+offset), 0, array[i]);
offset++;
}
}
I'm aware this is pretty horrible and that looping three times should not be necessary. But I've been trying lots of different methods, some working when reordering in one direction, some in the other an mostly, not at all. I figured I would look to optimize the function later, once I have it working with accuracy.
I'm certain I must be doing something extremely stupid or completely overlooking something, but for the life of me I can't work out what right now.
If you don't care about order of indexes array, I'd suggest another short solution:
items.splice.apply(items, [insertIndex, 0].concat(indexes.sort(function(a, b) {
return a - b;
})).map(function(i, p) {
return p > 1 ? items.splice(i - p + 2, 1).pop() : i;
}));
DEMO: http://jsfiddle.net/T83fB/
To make it short I used Array.map() method, which is not supported by old IE browsers. However it is always easy to use a shim from MDN.
You can use the .splice() function to add elements to an array, as well as removing items from it. The general principle is:
Sort indexes into ascending numeric order
Iterate over indexes, removing the element at that index (adjusting for the number of removed items) and storing it in a removedItems array
Add the removedItems array back in at the required index
The code to do that would look something like this:
var removedItems = [];
// sort indexes
indexes.sort(function(a, b) {
if(a < b) return -1;
else if(b < a) return 1;
return 0;
});
for(var i = 0; i < indexes.length; i++) {
var index = indexes[i];
removedItems.push(items.splice(index - removedItems.length, 1));
}
var insertIndex = 1;
items.splice.apply(items, [insertIndex, 0].concat(removedItems));
Take a look at this jsFiddle demo.
Let's say I have a dynamic array that is populated on page load with various amounts of strings inside:
var arr = ["string1","string2","string3","string4","string5","string6","string7","string8","string9","string10","string11","string12","string13","string14","string15","string16","string17","string18"];
I then have a function that is called on each event (let's say a click) that needs to bring back 3 strings of the array consecutively while remembering which value it left off at, the last time it was called. So:
First time function is called, it returns:
string1, string2, string3
Second time it is called, it returns:
string4, string5, string6
and so on...
I don't the need the code for the click event or the callback function, rather the code for the function that would generate the extraction each time and bring it back. Something simple like being able to call:
arr.runExtraction();
on each callback and having it bring back the desired data.
What should happen if the array is exhausted? Start from the beginning?
You could do something like this:
function get_iterator(values, steps, start) {
steps = steps || 1;
var current = start || 0,
max = values.length;
return function() {
var end = current+steps,
end = end > max ? max : end,
t = values.slice(current, end);
current = end % max;
// or if you don't want to wrap around:
// current = end;
return t;
}
}
Edit: Added start parameter.
As current will be the same as values.length in the end, splice will return an empty array if you don't wrap around.
Using slice won't change the original array.
And then
var extract = get_iterator(arr, 3);
var arr1 = extract(); // gives you the first three elements
var arr2 = extract(); // gives you the next three elements etc.
DEMO
It might give you less elements in the "last" extraction if the number of elements is not divisible by number of extracted elements. The next call will let it start from the beginning again. You could also modify it that it wraps around and takes elements from the beginning so that it will always return as many elements as you have specified.
Reference: Array.prototype.slice
var arr = ["string1","string2","string3","string4","string5","string6","string7","string8","string9","string10","string11","string12","string13","string14","string15","string16","string17","string18"];
var runExtraction = function () {
return arr.splice(0, 3);
}