Iterate through a single column of HTML table efficiently and properly - javascript

You may want to look for the check() function HERE
function check(){
var i = 0;
var s = 0;
for(i = 0; i < tbody.rows.length; i++){
if(tbody.childNodes[i].childNodes[0].textContent == "unit_3"){
s = i;
}
}
return s ;
}
I did this function to iterate through a specific column of my table. I was wondering since I don't know much about Javascript and iterations if there is a better & faster way to look for a specific value in a column because in a real case I may have actually a lot of rows so I want the iteration to be fast. I suck with jQuery but any suggestion is welcome :)
Thank you in advance

ECMAScript 6 introduces findIndex (which can be polyfilled):
function check() {
return [].findIndex.call(tbody.rows, function(row) {
return row.cells[0].textContent == "unit_3";
});
}

Related

javascript callback - how to use call backs

I need help with this JS callback function. I am trying to figure out how exactly callbacks work in JS.
--quick test code follows:
function debFilter(deb_array, fillCb){
var filt_darr = [];
for (var inx in deb_array) {
filt_darr.push(fillCb(deb_array[inx]));
}
return filt_darr;
}
console.log(debFilter(savedInp, function(x) { if (x%2 == 0) { return x;}} ));
Let's say my savedInp array contains [2,3,4,5,6,7,8,9] something like this. How do I make sure my callback returns only the even elements and not the odd ones? so my filt_darr would be [2,4,6...etc].
With the above test code I am getting [2,undefined,4,undefined,..etc]. I have tried with other similar conditions too with no avail. I just need to know how to tell JS not to 'push/return' something I dont need. Sorry if this is a beginner Q.
Thanks for the help.
Iterate the array and then push evens into a new array:
var a = [1,2,3,4,5];
function getEvens(originalArray){
var evens = [];
for(var i = 0; i < originalArray.length; ++i){
if(originalArray[i] % 2 === 0){
evens.push(originalArray[i]);
}
}
return evens;
}
As you probably noticed, you are collecting every return value into your result array and your callback returns undefined for every odd.
You could change your code to sth like
function debFilter(deb_array, fillCb){
'use strict';
var filt_darr = [],
len = deb_array.length;
for (var i = 0; i < len; i++) {
if (fillCb(deb_array[i])) {
filt_darr.push(deb_array[i]);
}
}
return filt_darr;
}
By the way, ES 5 supports Array.prototype.filter which might be what you are looking for. There is also a polyfill that you can take some inspiration from.

Jquery .clone() method. Removing clones

I noticed some interesting behavior when dealing with the .clone() function.
If I have a function to create rows and columns dynamically like this:
function appendDiv(n) {
for (var i=0;i<n;i++) {
$rows.append($columns.clone()); //assume I put $('.rows') & others in a var
}
for (var i=0;i<n;i++) {
$wrapper.append($rows.clone());
}
}
And I then delete the elements from the DOM maybe like this:
function deleteClones() {
$wrapper.off();
$wrapper.html('');
$('body').append($wrapper);
num = prompt("Enter another number.");
return num;
}
So I'd be calling the functions in an order like this:
appendDiv(num);
num = deleteClones();
appendDiv(num);
Can someone tell me why when I call appendDiv(num); again after removing those elements, the old columns are added along with the new ones? Here is a preschool level demonstration of what I mean: http://jsfiddle.net/wj6sgeeu/. Notice upon inspecting the html document, the clones that were created before we called deleteClones() are added again when we call appendDiv(num) for the second time.
I'm new to jquery, so maybe this is a self evident and obvious fact (maybe using a different method to remove clones?) but does someone have an explanation for this behavior?
Thank you!
You need to remove the previously added columns from the row, else you are just keep adding more columns to the existing columns
function appendDiv(n) {
$rows.empty();
for (var i = 0; i < n; i++) {
$rows.append($columns.clone());
console.log('ok');
}
for (var i = 0; i < n; i++) {
$wrapper.append($rows.clone());
console.log('ok2');
}
}
Demo: Fiddle

How to match and remove an object from javascript array?

I am trying to delete an element based on string match for a object property but when I do a slice on the javascript array the array size decreases and indexes change. Please help e with a solution. Here is a jsfiddle link for the same.
Code
var selection = JSON.parse('[{"Connectors":"c1"},{"Connectors":"c2"},{"Schedules":"s1"},{"Schedules":"s2"},{"Gauges":"g1"},{"Gauges":"g2"},{"Gauges":"g3"}]');
removeitem("Gauges");
function removeitem(item) {
for (var i = 0; i < selection.length; i++) {
if (selection[i].hasOwnProperty(item)) {
selection.splice(i, 1);
}
}
}
Add i--;
function removeitem(item) {
for (var i = 0; i < selection.length; i++) {
if (selection[i].hasOwnProperty(item)) {
selection.splice(i, 1);
i--;
}
}
}
jsfiddle example
Assuming you don't have a problem with having undefined as the new value, then you could call delete[i]; instead of selection.splice(i, 1); in that case the length does not change and neither will the indices.
Both Abhi1964 and Loolooii solution seems to work fine and solve problem, but i would personally keep the filtered results in separate array instead of manipulating index/deleting value in the same array, reason being, separate array would make code look simpler to read and understand. Reviewer need not to understand the index manipulation or keep track of undefined.
var selection = JSON.parse('[{"Connectors":"c1"},{"Connectors":"c2"},{"Schedules":"s1"},{"Schedules":"s2"},{"Gauges":"g1"},{"Gauges":"g2"},{"Gauges":"g3"}]');
removeitem("Gauges");
var filteredResult = [];
function removeitem(item) {
for (var i = 0; i < selection.length; i++) {
if (selection[i].hasOwnProperty(item)) {
}else{
filteredResult.push(item);
}
}
}
//use filtered result as per your need.
Note:
I have not run this code, if some error seems to be there, please feel free to edit.

JavaScript array sort(function) to sort table rows- not sorting

I am trying to sort a dynamically constructed table on the client side. So far I have done my research to discover JavaScript's sort() method will take a callback. Here is what I have so far:
function retrvCatalog(e){
var merch = document.getElementById('merch');
var tRows = merch.rows;
var tBody = merch.tBodies;
var rowArr = [];
for (x in tRows){
rowArr[x] = tRows[x];
}
rowArr.sort(function(a, b){
if (a.cells.textContent < b.cells.textContent){
return -1;
}
if(a.cells.textContent > b.cells.textContent){
return 1;
}
return 0;
});
}
Stepping through it in Firebug, it appears to not change the order of the rows. Can someone please help me figure out what I am missing?
FINAL ALGORITHM
function retrvCatalog(e){
var fltr = e.id;
var merch = document.getElementById('merch');
var tblHead = merch.tHead;
merch.deleteTHead();
var tRows = merch.rows;
var rowArr = [];
for (var i=0; i<tRows.length; i++){
rowArr[i] = tRows[i];
}
rowArr = rowArr.sort(function(a, b){
if (fltr > 3){
a = parseFloat(a.cells[fltr].innerHTML);
b = parseFloat(b.cells[fltr].innerHTML);
}
else{
a = a.cells[fltr].innerHTML;
b = b.cells[fltr].innerHTML;
}
if (a>b){
return 1;
}
if(a<b){
return -1;
}
return 0;
});
while(merch.hasChildNodes()) {
merch.removeChild(merch.firstChild);
}
merch.appendChild(tblHead);
for (i=0;i<rowArr.length;i++){
merch.appendChild(rowArr[i]);
}
}
The final two columns in the row are numbers, so that is why the method to sort is slightly variable.
Several problems in your code.
First, you didn't declare the x variable.
for(var x...
Second, don't use for-in to iterate an array like collection. Use for.
for (var x = 0, len = tRows.length; x < len; x++){
rowArr[x] = tRows[x];
}
Third, there is no textContent property of a cells collection.
This is easy to test by logging its value. This should have been the first thing you tried.
console.log(a.cells.textContent); // undefined
You need to decide which cell you want, and ask for it by index.
console.log(a.cells[0].textContent);
Finally, you should be aware that this technique will not show the result of the sorting in the DOM. You're only sorting the Array. You'll need to append the new ordering to the DOM.
Maybe you knew this, but you didn't show it in your code.
I don't know the relationship of the rows to the tBodies, so I can't give an example. But if all the rows are in one tbody, just loop the Array, and tBody[0].appendChild(rowArr[i])
I'm not sure if I'm missing something, but I'm pretty sure you can't use textContent on the cells array. You need to index cells so you know which column to actually sort on. If your rows have 4 columns each (or even if there's only 1), you still need to tell the sort function which column to sort on.
So in your sort function, if you wanted to sort by the second column, you'd want something like:
rowArr.sort(function (a, b) {
if (a.cells[1].textContent < b.cells[1].textContent) {
return -1;
} else if (a.cells[1].textContent > b.cells[1].textContent) {
return 1;
}
return 0;
});
And I'm not sure what's in your cells, but you may want to use .innerHTML, not .textContent.
rowArr.sort(function(a,b) {
a = parseFloat(a.cells.textContent);
b = parseFloat(b.cells.textContent);
return (a-b);
};
"don't use for-in to iterate an array like collection." - user1673729
tRows is not an array, it's an HTML collection. That is why I used "for in" – nodirtyrockstar
An HTML Collection is an array like collection. Do not use for-in.

Better way to see if an array contains an object?

I have an array of items (terms), which will be put as <option> tags in a <select>. If any of these items are in another array (termsAlreadyTaking), they should be removed first. Here is how I have done it:
// If the user has a term like "Fall 2010" already selected, we don't need that in the list of terms to add.
for (var i = 0; i < terms.length; i++)
{
for (var iAlreadyTaking = 0; iAlreadyTaking < termsAlreadyTaking.length; iAlreadyTaking++)
{
if (terms[i]['pk'] == termsAlreadyTaking[iAlreadyTaking]['pk'])
{
terms.splice(i, 1); // remove terms[i] without leaving a hole in the array
continue;
}
}
}
Is there a better way to do this? It feels a bit clumsy.
I'm using jQuery, if it makes a difference.
UPDATE Based on #Matthew Flaschen's answer:
// If the user has a term like "Fall 2010" already selected, we don't need that in the list of terms to add.
var options_for_selector = $.grep(all_possible_choices, function(elem)
{
var already_chosen = false;
$.each(response_chosen_items, function(index, chosen_elem)
{
if (chosen_elem['pk'] == elem['pk'])
{
already_chosen = true;
return;
}
});
return ! already_chosen;
});
The reason it gets a bit more verbose in the middle is that $.inArray() is returning false, because the duplicates I'm looking for don't strictly equal one another in the == sense. However, all their values are the same. Can I make this more concise?
var terms = $.grep(terms, function(el)
{
return $.inArray(el, termsAlreadyTaking) == -1;
});
This still has m * n performance (m and n are the lengths of the arrays), but it shouldn't be a big deal as long as they're relatively small. To get m + n, you could use a hashtable
Note that ECMAScript provides the similar Array.filter and Array.indexOf. However, they're not implemented in all browsers yet, so you would have to use the MDC implementations as a fallback. Since you're using jQuery, grep and inArray (which uses native indexOf when available) are easier.
EDIT:
You could do:
var response_chosen_pk = $.map(response_chosen_items, function(elem)
{
return elem.pk;
});
var options_for_selector = $.grep(all_possible_choices, function(elem)
{
return $.inArray(elem.pk, response_chosen_pk) == -1;
});
http://github.com/danstocker/jorder
Create a jOrder table on termsAlreadyTaking, and index it with pk.
var table = jOrder(termsAlreadyTaking)
.index('pk', ['pk']);
Then you can search a lot faster:
...
if ([] == table.where([{ pk: terms[i].pk }]))
{
...
}
...

Categories

Resources