jQuery - Selectively storing table data to 2d array - javascript

I came up with the problem of storing html table data to a 2D array. Instead of using nested loops, I was looking for jquery functions that would allow me to do the same. I found this method which seems to do the job.
var tRows = $('tr').map(function() {
return [$(this).find('td').map(function() {
return $(this).text()
}).get()]
}).get()
console.log(tRows)
I'd like to know, how I can use a condition, in the given code, to check if the first td of a tris empty or not (or some other condition in general) and thus not add that row to the final array.
EDIT:
Reading the comments and building upon this fiddle, I now have
var tRows = $('tr').map(function() {
var arr = $(this).find('td').map(function() {
return $(this).text()
}).get();
if (arr[0].length) return[arr];
}).get()
This works somewhat, but I was hoping to check the condition in the inner map and return a NULL instead of the entire array. From the discussion here, it seems that it is not possible to abort the inner map call midway, and it will return the entire array. Seems like loops might be better in the end for this job, or did I miss something?

Assuming the code you posted is working for you...
If you want the .text of the first td not empty of each row, the use of a combination of CSS selectors may be the solution.
:first-child
:not
:empty
EDIT
Okay... I improved it a little.
Seem like there can ba some spaces in an empty td...
So I used a regex to test it.
Here are the new things I used:
:first
.test() JavaScript method
.trim() JavaScript method
And the CodePen where I tryed it.
$(document).ready(function(){
console.clear();
console.log("loading...");
var tRows = $('tr').map(function() {
return [$(this).find('td:first').map(function() {
var pattern = /^\s+$/;
if($(this).text()!="" && !pattern.test($(this).text())){
return $(this).text().trim();
}
}).get()]
}).get()
console.log(tRows)
});
This return: [["blah1],[],[blah7]], in the CodePen...
If you do not want the empty one in the middle:
Second CodePen
$(document).ready(function(){
console.clear();
console.log("loading...");
var tRows = $('tr').map(function() {
var arr = [$(this).find('td:first').map(function() {
var pattern = /^\s+$/;
if($(this).text()!="" && !pattern.test($(this).text())){
return $(this).text().trim();
}
}).get()];
if(arr[0]!=""){
return arr;
}
}).get()
console.log(tRows)
});
This return: [["blah1],[blah7]]
2nd EDIT
This can also be achieved this way.
(it implies only one loop and the code is more simple to read).
See 3rd CodePen
$(document).ready(function(){
console.clear();
console.log("loading...");
var arr=[];
var counter=0;
$('tr').each(function() {
var thisTDtext = [$(this).find("td").first().text().trim()];
if(thisTDtext!=""){
arr[counter] = thisTDtext;
counter++;
}
});
console.log(JSON.stringify(arr));
});
This also return: [["blah1],[blah7]]

Related

Two blocks in getElementById [duplicate]

doStuff(document.getElementById("myCircle1" "myCircle2" "myCircle3" "myCircle4"));
This doesn't work, so do I need a comma or semi-colon to make this work?
document.getElementById() only supports one name at a time and only returns a single node not an array of nodes. You have several different options:
You could implement your own function that takes multiple ids and returns multiple elements.
You could use document.querySelectorAll() that allows you to specify multiple ids in a CSS selector string .
You could put a common class names on all those nodes and use document.getElementsByClassName() with a single class name.
Examples of each option:
doStuff(document.querySelectorAll("#myCircle1, #myCircle2, #myCircle3, #myCircle4"));
or:
// put a common class on each object
doStuff(document.getElementsByClassName("circles"));
or:
function getElementsById(ids) {
var idList = ids.split(" ");
var results = [], item;
for (var i = 0; i < idList.length; i++) {
item = document.getElementById(idList[i]);
if (item) {
results.push(item);
}
}
return(results);
}
doStuff(getElementsById("myCircle1 myCircle2 myCircle3 myCircle4"));
This will not work, getElementById will query only one element by time.
You can use document.querySelectorAll("#myCircle1, #myCircle2") for querying more then one element.
ES6 or newer
With the new version of the JavaScript, you can also convert the results into an array to easily transverse it.
Example:
const elementsList = document.querySelectorAll("#myCircle1, #myCircle2");
const elementsArray = [...elementsList];
// Now you can use cool array prototypes
elementsArray.forEach(element => {
console.log(element);
});
How to query a list of IDs in ES6
Another easy way if you have an array of IDs is to use the language to build your query, example:
const ids = ['myCircle1', 'myCircle2', 'myCircle3'];
const elements = document.querySelectorAll(ids.map(id => `#${id}`).join(', '));
No, it won't work.
document.getElementById() method accepts only one argument.
However, you may always set classes to the elements and use getElementsByClassName() instead. Another option for modern browsers is to use querySelectorAll() method:
document.querySelectorAll("#myCircle1, #myCircle2, #myCircle3, #myCircle4");
I suggest using ES5 array methods:
["myCircle1","myCircle2","myCircle3","myCircle4"] // Array of IDs
.map(document.getElementById, document) // Array of elements
.forEach(doStuff);
Then doStuff will be called once for each element, and will receive 3 arguments: the element, the index of the element inside the array of elements, and the array of elements.
getElementByID is exactly that - get an element by id.
Maybe you want to give those elements a circle class and getElementsByClassName
document.getElementById() only takes one argument. You can give them a class name and use getElementsByClassName() .
Dunno if something like this works in js, in PHP and Python which i use quite often it is possible.
Maybe just use for loop like:
function doStuff(){
for(i=1; i<=4; i++){
var i = document.getElementById("myCiricle"+i);
}
}
Vulgo has the right idea on this thread. I believe his solution is the easiest of the bunch, although his answer could have been a little more in-depth. Here is something that worked for me. I have provided an example.
<h1 id="hello1">Hello World</h1>
<h2 id="hello2">Random</h2>
<button id="click">Click To Hide</button>
<script>
document.getElementById('click').addEventListener('click', function(){
doStuff();
});
function doStuff() {
for(var i=1; i<=2; i++){
var el = document.getElementById("hello" + i);
el.style.display = 'none';
}
}
</script>
Obviously just change the integers in the for loop to account for however many elements you are targeting, which in this example was 2.
The best way to do it, is to define a function, and pass it a parameter of the ID's name that you want to grab from the DOM, then every time you want to grab an ID and store it inside an array, then you can call the function
<p id="testing">Demo test!</p>
function grabbingId(element){
var storeId = document.getElementById(element);
return storeId;
}
grabbingId("testing").syle.color = "red";
You can use something like this whit array and for loop.
<p id='fisrt'>??????</p>
<p id='second'>??????</p>
<p id='third'>??????</p>
<p id='forth'>??????</p>
<p id='fifth'>??????</p>
<button id="change" onclick="changeColor()">color red</button>
<script>
var ids = ['fisrt','second','third','forth','fifth'];
function changeColor() {
for (var i = 0; i < ids.length; i++) {
document.getElementById(ids[i]).style.color='red';
}
}
</script>
For me worked flawles something like this
doStuff(
document.getElementById("myCircle1") ,
document.getElementById("myCircle2") ,
document.getElementById("myCircle3") ,
document.getElementById("myCircle4")
);
Use jQuery or similar to get access to the collection of elements in only one sentence. Of course, you need to put something like this in your html's "head" section:
<script type='text/javascript' src='url/to/my/jquery.1.xx.yy.js' ...>
So here is the magic:
.- First of all let's supose that you have some divs with IDs as you wrote, i.e.,
...some html...
<div id='MyCircle1'>some_inner_html_tags</div>
...more html...
<div id='MyCircle2'>more_html_tags_here</div>
...blabla...
<div id='MyCircleN'>more_and_more_tags_again</div>
...zzz...
.- With this 'spell' jQuery will return a collection of objects representing all div elements with IDs containing the entire string "myCircle" anywhere:
$("div[id*='myCircle']")
This is all! Note that you get rid of details like the numeric suffix, that you can manipulate all the divs in a single sentence, animate them... Voilá!
$("div[id*='myCircle']").addClass("myCircleDivClass").hide().fadeIn(1000);
Prove this in your browser's script console (press F12) right now!
As stated by jfriend00,
document.getElementById() only supports one name at a time and only returns a single node not an array of nodes.
However, here's some example code I created which you can give one or a comma separated list of id's. It will give you one or many elements in an array. If there are any errors, it will return an array with an Error as the only entry.
function safelyGetElementsByIds(ids){
if(typeof ids !== 'string') return new Error('ids must be a comma seperated string of ids or a single id string');
ids = ids.split(",");
let elements = [];
for(let i=0, len = ids.length; i<len; i++){
const currId = ids[i];
const currElement = (document.getElementById(currId) || new Error(currId + ' is not an HTML Element'));
if(currElement instanceof Error) return [currElement];
elements.push(currElement);
};
return elements;
}
safelyGetElementsByIds('realId1'); //returns [<HTML Element>]
safelyGetElementsByIds('fakeId1'); //returns [Error : fakeId1 is not an HTML Element]
safelyGetElementsByIds('realId1', 'realId2', 'realId3'); //returns [<HTML Element>,<HTML Element>,<HTML Element>]
safelyGetElementsByIds('realId1', 'realId2', 'fakeId3'); //returns [Error : fakeId3 is not an HTML Element]
If, like me, you want to create an or-like construction, where either of the elements is available on the page, you could use querySelector. querySelector tries locating the first id in the list, and if it can't be found continues to the next until it finds an element.
The difference with querySelectorAll is that it only finds a single element, so looping is not necessary.
document.querySelector('#myCircle1, #myCircle2, #myCircle3, #myCircle4');
here is the solution
if (
document.getElementById('73536573').value != '' &&
document.getElementById('1081743273').value != '' &&
document.getElementById('357118391').value != '' &&
document.getElementById('1238321094').value != '' &&
document.getElementById('1118122010').value != ''
) {
code
}
You can do it with document.getElementByID Here is how.
function dostuff (var here) {
if(add statment here) {
document.getElementById('First ID'));
document.getElementById('Second ID'));
}
}
There you go! xD

Using parent() in a for loop

I am creating a chrome extension that blocks all porn results on all torrent search engine sites.
So I am trying to retrieve the name of the torrents and check them against the array of strings containing blocked (adult/porn) words that I created. If it matches the array word then it should set the display of the parent element to none. But parent() from jQuery doesn't seem to work around this in a for loop. This is the code that I am using.
// 'blockedWords' is the array.
// '$("dl dt")' contains the words that I am checking against strings from
// the array 'blockedWords'.
for (var i = 0; i < $("dl dt").length; i++) {
for (var j = 0; j < blockedWords.length; j++) {
if($("dl dt")[i].innerText.indexOf(blockedWords[j]) > -1){
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
}
}
}
// 1st Method shows the error 'Cannot set property 'display' of undefined'
// 2nd Method shows the error '$(...)[i].parent is not a function'
// '$("dl dt")[i].parent().style.display' doesn't work but
// '$("dl dt").parent().style.display' doesn't work either
// '$("dl dt")[i].style.display' works perfectly without parent().
I have also tried 'parents()'.
Any help will be appreciated :).
As a newbie, I am also open to any other suggestions or recommendations.
And I would be really grateful if you could explain your code as well :)
And by the way, can you believe there are more than 500 porn companies out there :o :P :D
Since you have jQuery, you can avoid using nested for-loops using jQuery's filter() and JavaScript reduce(s,v):
// Filter function removes elements that return a false/falsey value like 0
$("dl dt").filter(function() {
// Save current element's innerText so we can use it within the reduce function
var str = $(this).text();
// Return sum of reduce function
return blockedWords.reduce(function(s, v) {
// For each item in blockedWords array, check whether it exists in the string. Add to total number of matches.
return s + !!~str.indexOf(v);
}, 0); // 0 = intial value of reduce function (number of matches)
}).parent().hide(); // Hide elements which pass through the filter function
Demo:
var blockedWords = [
'shit', 'fuck', 'sex'
];
$("dl dt").filter(function() {
var str = $(this).text();
return blockedWords.reduce(function(s, v) {
return s + !!~str.indexOf(v);
}, 0);
}).parent().hide();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<dl><dt>this is shit</dt></dl>
<dl><dt>this is okay</dt></dl>
<dl><dt>fuck this</dt></dl>
<dl><dt>no problem</dt></dl>
<dl><dt>sex videos</dt></dl>
EDIT: I apologize for the earlier answer if you saw it, as it was incomplete. I have also added a snippet for demonstration purposes. For further explanation of the reduce algorithm, check this answer out (basically it converts the value of indexOf to either a 0 or 1, because indexOf returns -1 if not found, or another 0-indexed integer of the position if found).
JQuery's parent function returns a JQuery object with the parent element inside of it. If you want to access the element from this object you need to retrieve the element from the object using the bracket notation.
If you were to provide some HTML I would be able to test this and make sure it works, but here is some code that could get you pointed in the right direction to use mostly JQuery instead of relying on for loops with JavaScript.
JQuery Rewrite
$("dl dt").each(function(index, element){
if($.inArray(blockedWords,$(element).text()) > -1) {
$(this).parent().css("display", "block");
$(element).parent().css("display", "block");
}
})
The Answer To Your Specific Question
Change this:
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
to this:
$(this).parent()[0].style.display= "none"; // 1st Method or
$($("dl dt")[i]).parent()[0].style.display= "none"; // 2nd Method
optionally, you can instead use JQuery's css function like this:
$(this).parent().css("display", "none"); // 1st Method or
$($("dl dt")[i]).parent().css("display","none"); // 2nd Method

How to compare if an HTML element exists in the node array?

selectedContentWrap: HTML nodes.
htmlVarTag: is an string.
How do I check if the HTML element exists in the nodes?
The htmlVarTag is a string and don't understand how to convert it so it check again if there is a tag like that so that if there is I can remove it?
here is output of my nodes that is stored in selectedContentWrap
var checkingElement = $scope.checkIfHTMLinside(selectedContentWrap,htmlVarTag );
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
var node = htmlVarTag.parentNode;
while (node != null) {
if (node == selectedContentWrap) {
return true;
}
node = node.parentNode;
}
return false;
}
Well if you could paste the content of selectedContentWrap I would be able to test this code, but I think this would work
// Code goes here
var checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
for (item of selectedContentWrap) {
if (item.nodeName.toLowerCase() == htmlVarTag.toLowerCase()){
return true;
}
}
return false;
}
Simplest is use angular.element which is a subset of jQuery compatible methods
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
// use filter() on array and return filtered array length as boolean
return selectedContentWrap.filter(function(str){
// return length of tag collection found as boolean
return angular.element('<div>').append(str).find(htmlVarTag).length
}).length;
});
Still not 100% clear if objective is only to look for a specific tag or any tags (ie differentiate from text only)
Or as casually mentioned to actually remove the tag
If you want to remove the tag it's not clear if you simply want to unwrap it or remove it's content also ... both easily achieved using angular.element
Try using: node.innerHTML and checking against that
is it me or post a question on stackoverflow and 20min after test testing I figure it.,...
the answer is that in the selectedContentWrap I already got list of nodes, all I need to do i compare , so a simple if for loop will fit.
To compare the names I just need to use .nodeName as that works cross browser ( correct me if I am wrong)
Some dev say that "dictionary of tag names and anonymous closures instead" - but couldn't find anything. If anyone has this library could you please post it to the question?
here is my code.
var node = selectedContentWrap;
console.log('node that is selectedwrapper', selectedContentWrap)
for (var i = 0; i < selectedContentWrap.length; i++) {
console.log('tag name is ',selectedContentWrap[i].nodeName);
var temptagname = selectedContentWrap[i].nodeName; // for debugging
if(selectedContentWrap[i].nodeName == 'B' ){
console.log('contains element B');
}
}

I want object keys to check for duplicates, but I also want to sort the objects later, what should I do?

I have an array called newposts
I itterate through it and if it meets certain criteria, I add it to another array:
for (newpost in newposts){
if (!( newposts[newpost][0] in currentposts )){
var triploc1 = locations[newposts[newpost][1]].location;
var triploc2 = locations[newposts[newpost][2]].location;
var detour_distance = fourpoint_distance(newloc1, triploc1, triploc2, newloc2);
if (worthwhile_detour(original_distance,detour_distance)){
currentposts.push(posts[newposts[newpost][0]])
}
}
}
The second row, is intended to check for duplicates(newposts[newpost][0]) is an ID. When I wrote it I had forgotten that currentposts was an array. Obviously, this doesn't work. I need currentposts to be an array, because just below i sort it. I could ofcourse convert it into an array once the selection is done. But I'm new to javascript and believe someone might know a better way to do this.
function sortposts(my_posts){
my_posts.sort(function(a, b) {
var acount = a.sortvar;
var bcount = b.sortvar;
return (bcount-acount);
});
}
I'm not exactly sure what your desired goal here is, but I can try and clean this up for you. Please note that I'm using the underscore.js library as it makes working with arrays a HECK OF A LOT easier :) If you can't include underscore.js into your project, let me know and I'll write it in "pure" javascript :)
_.each(newposts, function(item) {
if ( _.indexOf(currentposts, posts[item[0]]) >= 0 ) {
var triploc1 = locations[item[1]].location;
var triploc2 = locations[item[2]].location;
var detour_distance = fourpoint_distance(newloc1, triploc1, triploc2, newloc2);
if (worthwhile_detour(original_distance, detour_distance)){
currentposts.push(posts[item[0]])
}
}
});
_.sortBy(currentposts, function(item) {
return item.sortvar;
});
I have to question, however, why you're using so many arrays (newposts, locations, posts, etc)? Are they all needed?

jquery/javascript: arrays

I am a begginer with Javascript/jQuery and I hope someone can help me with the following:
I have a simple form (7 questions; 3 radio buttons/answers per question - except for question 5 with 8 possible choices ) and based on the selected answers, when user clicks on 'view-advice' I want to display relevant advices (combination of 38 possible advices) below the form.
I have given "a", "b", "c",... values to radio buttons and I am collecting them in an array.
The part where the script alerts the array works ok.
I can't figure out the part where I display the advices depending on the values in the array.
I'd appreciate your help! Thanks!
Here is the code:
var laArray = new Array();
$('.button-show-advice').click(function(){
$(":radio:checked").each(function(i){
laArray[i] = $(this).val();
if (laArray == ["a","d","g","j","m","u"]) {
$("#advice-container, #advice1, #advice2").show(); // something is wrong here :(
};
})
alert(laArray) // testing to see if it works
})
Rather than test for equality, I think the better means is to check whether or not each of your values are in the array using the jQuery inArray function.
Granted, this is just the beginning of code. You could probably write a function to shore this up, like so.
function radioSelected(val) {
return ($.inArray(val, laArray) != -1);
}
and adapt it to your existing script.
You cannot compare arrays this way you should probably
either compare each element of the 2 arrays
function compare_array(array1,array2) {
var i;
for(i=0;i=array1.length;i++) {
if(array1[i]==array2[i]) {
return false;
}
}
return true;
}
or serialize the array in a comparable form ( comma separated string for example )
function compare_array(array1,array2) {
return array1.join(",")==array2.join(",");
}
Would be nice to see the HTML code. But I guess you want to do something like this:
var laArray = [];
var compareValues = function(arr1, arr2) {
$(arr1).each(function(index, el) {
if(el !== arr2[index]) {
return false;
}
});
return true;
};
$('.button-show-advice').click(function(){
$(":radio:checked").each(function(i){
laArray.push($(this).val());
});
if(compareValues(laArray,["a","d","g","j","m","u"])) {
$("#advice-container, #advice1, #advice2").show();
}
});
EDIT: updated the code, forgot the }); ...

Categories