How to map within a for loop - javascript

We need to map an object array within a for loop, which actually works, but the editor is giving us a warning saying not to put a function within a loop:
for(var i=0; i<$scope.data.list.length; i++){
$scope.data.list[i].isRowSelected=false;
var pos1 = $scope.selectedItems.map(function(e) { return e.sys_id; }).indexOf($scope.data.list[i].sys_id);
if(pos1!==-1){
var add = $scope.selectedItems.indexOf($scope.data.list[i].sys_id);
$scope.selectedItems.splice(add,1);
}
}
To mitigate this, we're thinking about creating a separate function for the mapping and then calling it within the loop, like this:
function mappingID(e){
return e.sys_id;
}
However, when we call upon it within the loop, we're lost as to what to pass in...any suggestions? Thanks!

two things, create a function outside the loop and avoid repeating indexing and object nesting. It will make your code much cleaner and easier to reason about. I'm pretty sure this whole function could be done a lot better but I'm not sure of the bigger scope
var items = $scope.selectedItems;
var sys_id = function(e) { return e.sys_id; }
for(var i=0; i<$scope.data.list.length; i++){
var data = $scope.data.list[i]; // might be a better name for this...
data.isRowSelected=false;
var pos1 = items.map(sys_id).indexOf(data.sys_id);
if(pos1!==-1){
var add = items.indexOf(data.sys_id);
items.splice(add,1);
}
}

The comments suggest lodash, which is a good suggestion. For the purposes of your original question, however, you can declare the function mappingID as you have it, and simply put
var pos1 = $scope.selectedItems.map(mappingID).indexOf($scope.data.list[i].sys_id);
and that will do the job.

You don't need to bring lodash to handle this, you can use find: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
for(var i=0; i<$scope.data.list.length; i++){
$scope.data.list[i].isRowSelected=false;
var item = $scope.selectedItems.find(e => (e.sys_id === $scope.data.list[i].sys_id));
if (item) {
$scope.selectedItems.splice(item,1);
}
}
Also I suggest changing selectedItems to an plain-object/Map/Set so you can lookup in constant time.

To avoid doing the same mapping on each iteration of the loop, move the mapping outside the loop:
var idArr = $scope.selectedItems.map(function(e) { return e.sys_id; })
$scope.data.list.forEach(item => {
item.isRowSelected=false;
var pos1 = idArr.indexOf(item.sys_id);
if(pos1!==-1){
var add = $scope.selectedItems.indexOf(item.sys_id);
$scope.selectedItems.splice(add,1);
}
})

Related

getElementByID loop returning null

Chrome developer tools says that the value function doesn't work on a null value and points to the line in the for loop. Why isn't getElementByID fetching my values? (this is a refactor, getElement work perfect with the actual values typed in).
locationStops = ["start","end"];
var stopNum = locationStops.length;
var stopAddresses = [];
for(val in locationStops) {
stopAddresses.push(document.getElementById(val).value);
}
You could avoid the for loop, and the potential for bugs that you ran into with, by using map:
stopAddresses = locationStops . map(function(id) {
return document.getElementById(id).value;
});
Depending on your stylistic preferences, you might find the following more readable:
function get_value_from_id(id) {
return document.getElementById(id).value;
}
stopAddresses = locationStops . map(get_value_from_id);
If you want to use a loop, you could use the new for...of construct:
for (let val of locationStops) {
^^
stopAddresses.push(document.getElementById(val).value);
}
If you have an environment that supports ES7 array comprehensions:
[ for (id of locationStops) document.getElementById(id).value ]
If you want to stick with your for...in loop, then as other answers have pointed out, the loop variable is the index, not the value, so you have to access the ID with locationStops[i], but you are better off using a regular for loop.
Do not use for in for arrays.
Use a simple for loop instead.
var a = ["start", "end"];
for(var i = 0; i < a.length; ++i)
{
console.log(document.getElementById(a[i]).value);
}
You can use for-in also but, it is not recommended as it results in unexpected behaviour sometimes.
val refers to 0,1 etc. So there must be elements with ID's 0,1.
for(var val in a)
{
console.log(document.getElementById(a[val]).value);
}
Your code is not working because your for loop syntax is incorrect
Try This
var locationStops = ["start","end"];
var stopNum = locationStops.length;
var stopAddresses = [];
for(i = 0; i < locationStops.length; i++) {
stopAddresses.push(document.getElementById(locationStops[i]).value);
}
Alternatively, you could use Array.prototype.map.
var locationStops = ["start","end"];
var stopAddresses = locationStops.map(function(val) {
return document.getElementById(val).value;
});
Honestly, though, looping over a two element array is kind of silly and if it was my code I would even prefer to simply assign each address directly.
var stopAddresses = [document.getElementById("start").value, document.getElementById("end").value];

How to avoid using filter twice against the same set of elements

I'm trying to separate two types of inputs into their own jQuery wrapped sets as they need to be processed differently depending on whether the id contain '-add-new-' or not. I know I could do this using filter twice as follows:
var seriesTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') == -1;
});
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') >= 0;
});
However filtering twice seems inefficient to me as I know it will require a second loop. Is there a way to avoid this?
Try like below:
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]:not([id*="-add-new-"])');
OR
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]').not(addNewTabInputs);
Just to offer an alternative to using specific selectors, you could iterate through the jQuery set and build the two collections as you go. I don't know that this would be any faster due to the different operations applied to the collections.
var $inputs = $msSeriesTabs.find('input');
var seriesTabInputs = [];
var addNewTabInputs = [];
for (var i = 0; i < $inputs.length ; i += 1)
{
var input = $($inputs[i]);
if ( $(input).attr('id').indexOf('-add-new-') >= 0 )
{ addNewTabInputs.push(input); }
else
{ seriesTabInputs.push(input); }
}
seriesTabInputs = $(seriesTabInputs);
addNewTabInputs = $(addNewTabInputs);
Avoiding filtering twice may not be so crucial unless you are dealing with an enormous amount of elements. Furthermore there is something to be said for the consistency of the code when you filter twice.
That being said there is a way to avoid filtering twice and it may even be instructional; below is some code that can be used to achieve this.
First, we create an empty wrapped set that can be added to, this is achieved by var seriesTabInputs = $(false); Please see this write-up for more information.
Then inside of the filter, we conditionally add to seriesTabInputs but note the way in which we do it: we continually re-assign with seriesTabInputs = seriesTabInputs.add($(this)); If instead you merely call seriesTabInputs.add($(this)) without assigning to seriesTabInput you will wind up with an empty array in the end. Please see the jQuery docs for .add() which gives a similar incorrect example and states that such usage "will not save the added elements, because the .add() method creates a new set".
var seriesTabInputs = $(false);
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
if ($(this).attr('id').indexOf('-add-new') >= 0) {
return true;
}
else {
seriesTabInputs = seriesTabInputs.add($(this));
}
});

Difference of defining an array inside and outside a for loop

the title is already clear, what's the difference between die create an array inside or outside a for loop.
I will give you an example.
var studentsarray = [];
for(var i = 0; i < 5; i++){
var students = {
id:i,
roll:"9",
age:13
}//end students
studentsarray.push(students);
localStorage.setItem('veritabani', JSON.stringify(studentsarray));
}//end for
var aldim = $.parseJSON(localStorage.getItem('veritabani'));
$.each(aldim, function(i,item){
alert(item.id);
});
if i define inside a for loop, i can't reach all elements, but if i define outside the for loop, it is only the last value of(id) displayed.
Can you explain why?
Thanks in advance.
Few observations:
Javascript only has function level scoping, so defining a variable inside the for loop is equivalent to defining it outside.
However, the variable assignment will happen multiple times if inside the for loop
Please note JSON.stringify is setting studentarray by value multiple times, did you really mean to do this inside the for loop?
I wonder if this is really what you meant to do?
var studentsarray = [];
for(var i = 0; i < 5; i++){
studentsarray.push({id: i, roll:"9", age:13 });
}
localStorage.setItem('veritabani', JSON.stringify(studentsarray));
var aldim = $.parseJSON(localStorage.getItem('veritabani'));
$.each(aldim, function(i,item){
alert(item.id);
});

What is the fastest / recommended way of creating new list with duplicate list entries in JavaScript?

My values naturally come in this form:
[1,2,3,4,5,6,7,8,9]
I am developing against a server api which requires an input parameter like:
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
Is there a faster or more js-style way to do than a simple for loop?
var f = function(values) {
var newList = [];
var i;
for (i = 0; i < values.length; i++) {
newList.push(values[i]);
newList.push(values[i]);
}
return newList;
}
You could avoid a .push() call by combining them since .push() is variadic.
newList.push(values[i], values[i]);
Other than that, I doubt you'll get much quicker.
You can use each function.
this will reduce your step.
var list=[1,2,3,4,5,6,7,8,9]
var newlist=[];
$.each(list,function(index,data){newlist.push(data);newlist.push(data)})
hope this helps.
May be assignment is faster...
L2 = [];
for (var i=L1.lenght*2; i-->0;) {
L2[i>>1] = L1[i];
}
but this kind of micro-optimization really needs to be profiled on the specific implementations (and I wouldn't be surprised in big differences between different Javascript engines).
I'd keep the most readable way unless this is a key issue (and if it's a key issue they probably Javascript is the wrong tool).
Try: [].concat.call([1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,9]).sort();
or more generic:
(function(){
return this.concat(this).sort(function(a,b){return a-b;});}
).call([1,2,10,20,30,40,50,60,70,80,9]);
or just a function:
function(v) {
var i = v.length, nw = [];
while (i--) { nw.push(v[i],v[i]); }
return nw.reverse();
}
or using map
var nw = ([1,2,3,4,5].map(function(a){this.push(a,a)},nw=[]),nw);
I suspect the function is the most efficient.

JavaScript converting an array to array of functions

Hello I'm working on a problem that requires me to change an set array of numbers into an array that returns the original numbers as a function. So we get a return of a2 instead of a[2].
I dont want the answer I just need a hint. I know i can loop through the array and use .pop() to get the last value of the array, but then I dont know how to convert it to a function from there. any hints?
var numToFun = [1, 2, 3];
var numToFunLength = numToFun.length;
for (var i = 0; i < numToFunLength; i++) {
(function(num){
numToFun.unshift(function() {
return num;
});
}(numToFun.pop()))
}
DEMO
basically it pops out a number from the last, builds a function with that number returned, and put back into the first of the array. after one full cycle, all of them are functions.
here's the catch: how this works, it's up to you to research
why the loop does not look like the straightforward pop-unshift:
for (var i = 0; i < numToFunLength; i++) {
numToFun.unshift(function() { //put into first a function
return numToFun.pop() //that returns a number
});
}
and why i did this: (HINT: performance)
var numToFunLength = numToFun.length;
There's three important steps here:
Extract the number value from the array. Within a loop with an iterator of i, it might look like this:
var num = numArray[i];
This is important, because i will not retain its value that it had when you created the new function - it'll end up with the last value it had, once the for loop is finished. The function itself might look like this:
function() { return num; }
There's no reference to i any more, which is important - to understand better, read about closures. The final step would be to add the new function to the array of functions that you want.
...and you're done!
EDIT: See other's answers for good explanations of how to do this right, I will fix mine also though
As others have pointed out, one of the tricky things in javascript that many struggle with (myself included, obviously) is that scoping variables in javascript is dissimilar to many other languages; scopes are almost purely defined by functions, not the {} blocks of, for example, a for loop, as java/C would be.
So, below you can see (and in other answers here) a scoping function can aid with such a problem.
var numArray = [12, 33, 55];
var funcArray = [];
var numArrLength = numArray.length; // Don't do this in for loop to avoid the check multiple times
for(var j=0; j < numArrLength; j++) {
var scopeMe = function() {
var numToReturn = numArray[j];
console.log('now loading... ' + numToReturn);
var newFunc = function() {
return numToReturn;
};
return newFunc;
}();
funcArray.push(scopeMe);
};
console.log('now me');
console.log(funcArray);
console.log(funcArray[0]());
console.log(funcArray[1]());
console.log(funcArray[2]());
console.log(funcArray[1]()); // To ensure it's repeatable
EDIT my old bad answer below
What you'll want to do is something like
var funcArray = [];
for(...) {
var newFunc = function() {
return numArray.pop();
}
funcArray.push(newFunc);
}
The key here is that functions in javascript can be named variables, and passed around as such :)

Categories

Resources