I have an array of strings. The strings in the array look like this:
The World
The World/Europe/
The World/Europe/Sweden/
The World/Europe /Sweden/Stockholm
The World/Europe/Sweden/Gothenburg
The World/Europe/Finland/
The World/Europe/Finland/Helsinki
I want to present this as an unordered list like this in HTML:
var myName = (function () {
function mkEl(a) {
var ul = document.createElement("ul");
if (a.every(function(v){return !/\//.test(v);})) {
a.filter(function(e) {
return e.length;
}).forEach(function(e) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(e));
ul.appendChild(li);
});
return ul;
}
a.map(function (e) {
return e.split("/");
}).filter(function (e,i,a) {
return (!i && e[0].length) || (i && e[0] != a[i-1][0]);
}).forEach(function(e) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(e[0]));
var r = new RegExp("^"+e[0]+"\/");
li.appendChild(mkEl(a.filter(function (e) {
return r.test(e);
}).map(function (e) {
return e.replace(r,"");
})));
ul.appendChild(li);
});
return ul;
}
return mkEl;
})();
That's the best I can come up with. It's a little complicated, but it's actually simpler than anything else I thought of using the DOM elements. If you need to support IE8 (as you very probably do), you'll need the array functions from MDC. I use, every, map, filter and forEach. slice has been supported for ages as far as I can remember.
BTW, I presume there's typo in your sample, it should be "The World/" right? If not, my code won't work.
EDIT: Well, I didn't think it would, but it does! In fact, by testing, and it seems logical, all you have to do is list the city names and full path and it'll generate the correct DOM tree.
Further addition, performance of something like this in JavaScript on a very large list is likely to be absolutely horrific, it could be optimised. For example, the .filter().map() combinations could be just one forEach, same with .filter().forEach() or if performance is really bad, just an old-fashioned iteration using a normal for loop. The a.map().filter().forEach() could be simplified into a few for loops too.
One final thing to note is that if you have an array like, ["The World/","The World/Europe","The World/Africa","The World/Europe/Finland"], you'll need to sort that before you pass it to mkEl, and myName can be anything of your choice, it won't affect the recursion.
Last edited by Declan1991; 11-09-2011 at 10:57 AM.
Reason: Correction & Addition
Great wit and madness are near allied, and fine a line their bounds divide.
It works exactly like I thought! Million thanks. I will now try to understand what the heck you did and try to do some of the kaizen you suggested (if it needs it). The final list will contain different data and be around 5000 lines long so we'll see about performance.
(function(){
// a string with a split at the end
var arr="The World,The World/Europe/,The World/Europe/Sweden/,The World/Europe/Sweden/Stockholm,The World/Europe/Sweden/Gothenburg,The World/Europe/Finland/,The World/Europe/Finland/Helsinki".split(/,/g);
c='';
for (var l=arr.length,i=0;i<l;i++) c+='<li>'+arr[i].replace(/([^\/]+\/)(?!$)/g,'')+'</li>\n';
c=c.replace(/\/</g,'<');
alert(c);
// to insert in some container...
// document.getElementById('myDiv').innerHTML='<ul>'+c+'</ul>';
})();
The regular expression replaces all the preceding motives with a negative assertion (?!$). It remains to eliminate the last conditionals slashes, what can be made on the whole string (if there is followed by <).
The whole is incorporated into an anonymous function executed at once.
Edit: Variant - with a comma or a dollar, the regular expression could be applied to the whole initial string !
Last edited by 007Julien; 11-11-2011 at 07:08 AM.
Reason: Complement
007Julien, that's not how I envisaged the code should work anyway, I know no other way of getting a nested list without some sort of recursion. I'm sure that there's a way to generate a string (rather than the DOM methods I used) in an easier fashion, but I'm just not sure right now.
Great wit and madness are near allied, and fine a line their bounds divide.
(function(){
// a simple string (obtained, for example, with one array.join(',');)
var arr="The World,The World/Europe/,The World/Europe/Sweden/,The World/Europe/Sweden/Stockholm,The World/Europe/Sweden/Gothenburg,The World/Europe/Finland/,The World/Europe/Finland/Helsinki";
var lst=arr.replace(/[^\/,]+\/(?!,)(?!$)/g,'').replace(/\//g,'').replace(/,/g,'</li><li>');
alert('<ul><li>'+lst+'</li></ul>');
})();
We remove at first, all sub-patterns which do not (^ at the beginning of the square brackets []) contains slash (/ with an backslash before) nor comma (,) one or more (+) following by a slash (/ with an other backslash before) and not following with a comma (?!,) nor following with the end of the string (?!$) ... Ouf !
EDIt : Sorry i miss the question. I had not seen the different lists...
There is nevertheless a solution with regular expressions
HTML Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html lang="fr"><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="generator" content="PSPad editor, www.pspad.com"><title>Untitled</title><style type="text/css"></style></head><body><p>The whished answer</p><ul><li>The world
<ul><li>Europe
<ul><li>Sweden
<ul><li>Stockholm
</li><li>Gothenburg
</li></ul></li><li>Finland
<ul><li>Helsinki
</li></ul></li></ul></li></ul></li></ul></div><p>The obtained answer</p><div id="rsl"></div><script type="text/javascript">
(function(){
// a string
var str="The World,The World/Europe/,The World/Europe/Sweden/,The World/Europe/Sweden/Stockholm,The World/Europe/Sweden/Gothenburg,The World/Europe/Finland/,The World/Europe/Finland/Helsinki";
var oldNiv=-1,newNiv;
var lst=str.replace(/([^,]+)(?=,|$)/g,function(a){var b;
a=a.replace(/\/$/g,''); // No more end slash
newNiv=a.replace(/[^\/]+/g,'').length;// The number of slashs give the level (or niveau)
b=a.replace(/([^\/]+\/)/g,'');// The last word(s)
if (oldNiv<newNiv) r='<ul><li>'+b+'</li>';
if (newNiv==oldNiv) r='<li>'+b+'</li>';
if (newNiv<oldNiv) r='</ul><li>'+b+'</li>';
oldNiv=newNiv;
return r});
while(-1<--newNiv) lst+='</ul></li>';
lst+='</ul>';
lst=lst.replace(/,/g,'');
alert(lst)
document.getElementById('rsl').innerHTML=lst;
})();
</script></body></html>
JavaScript replace method takes too functions as arguments !
<script type="text/javascript">
(function(){
// a string
var str="The World,The World/Europe/,The World/Europe/Sweden/,The World/Europe/Sweden/Stockholm,The World/Europe/Sweden/Gothenburg,The World/Europe/Finland/,The World/Europe/Finland/Helsinki";
var oldLvl=-1;newLvl;
var lst=str.replace(/([^,]+)(,|$)/g,function(t,a){var b;
a=a.replace(/\/$/g,''); // no more end slash
newLvl=a.replace(/[^\/]+/g,'').length;// the number of slashs give the level
b=a.replace(/([^\/]+\/)/g,'');// the last word
if (oldLvl<newLvl) r='\n<ul><li>'+b;
if (newLvl==oldLvl) r='</li>\n<li>'+b;
if (newLvl<oldLvl) r='</li></ul></li>\n<li>'+b;
oldLvl=newLvl;
return r});
while(-1<newLvl--) lst+='</li></ul>\n';
alert(lst)
document.getElementById('rsl').innerHTML=lst;
})();
</script>
<style type="text/css">
table td{ width:40px; border:1px solid red;}
</style>
<script type="text/javascript">
// http://www.webdeveloper.com/forum/showthread.php?t=253188
function fromTableToList(myid) {
var el= document.getElementById(myid);
var tr= el.getElementsByTagName('tr');
//alert(tr.length);
var B=[];
var C=[];
for(var i=0; i<tr.length; i++) {
var td=tr[i].getElementsByTagName('td');
B[i]=td.length;
C[i]= td[B[i]-1].innerHTML;
}
//alert(B);
//alert(C);
var liste="<ul><li>"+C[0];
for(var t=0,m=1; t<B.length; t++, m++) {
if(B[m]>B[t]) { liste+="<ul><li>"+C[m]; }
if(B[m]==B[t]) {liste+="</li><li>"+C[m]+"</li>";}
if(B[m]<B[t]){liste+="</ul></li><li>"+C[m];}
}
//alert(liste);
var ulli=liste.match(/<ul><li>/g).length;
//alert(ulli);// 5
var liul=liste.match(/<\/li><\/ul>/g).length;
//alert(liul); // 1
for(var k=0; k<(ulli-liul); k++) {
liste+= "</li></ul>"
}
//alert(liste);
var div=document.getElementById('myliste');
div.innerHTML= liste;
}
function makeTable() {
var s= "The World,The World/Europe/,The World/Europe/Sweden/,The World/Europe /Sweden/Stockholm,The World/Europe/Sweden/Gothenburg,The World/Europe/Finland/,The World/Europe/Finland/Helsinki";
var table= "<table id=\"mytable\">";
var A= s.split(",");
//alert(A);
for(var i=0; i<A.length; i++) {
table+="<tr>";
var n= A[i].match(/[^\/]+/g);
//alert(n);
for(var ii=0; ii<n.length; ii++) {
if(n[ii]!=n[n.length-1]) { table+= "<td></td>"; }
if(n[ii]==n[n.length-1]) {table+= "<td>"+n[ii]+"</td>";};
}
table+="</tr>";
}
table+="</table>";
//alert("table = "+table);
var mytablo=document.getElementById('mydiv');
mytablo.innerHTML=table;
}
</script>
<body>
<div id="mydiv"></div>
<input type="button" onclick="makeTable()" value="make table">
<div id="myliste"></div>
<input type="button" onclick="fromTableToList('mytable')" value="from table to list">
<p>The whished answer</p>
<ul>
<li>The world
<ul>
<li>Europe
<ul>
<li>Sweden
<ul>
<li>Stockholm</li>
<li>Gothenburg</li>
</ul>
</li>
<li>Finland
<ul>
<li>Helsinki</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
The Time Through Ages
In the Name of Allah, Most Gracious, Most Merciful
1. By the Time,
2. Verily Man is in loss,
3. Except such as have Faith, and do righteous deeds, and (join together) in the mutual enjoining of Truth, and of Patience and Constancy.
Bookmarks