Sha*_*haw 5 javascript xml xslt jquery
我现在遇到一个问题,这是我们团队无法控制的服务器当前限制的结果.
我们有一份应该由数据库完成的工作,但是我们被迫使用XML文件并使用Javascript/jQuery解析它.我们甚至没有脚本的写入权限(只能通过我们的FTP帐户)...我们不喜欢谈论它,但这就是我们得到的.
由于这些限制,问题在于我们需要解析大约500kb的大型XML文件,其中包含1700个文档名称/数字/ URL的记录.
这个数字非常复杂,例如"31-2b-1029E",与"T2315342"之类的东西混合在一起.
所以,我认为我需要使用一种名为"Natural Sort"的东西(谢谢stackoverflow).
无论如何,我试着在这里使用这个脚本:
/*
* Reference: http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm/
* Natural Sort algorithm for Javascript - Version 0.6 - Released under MIT license
* Author: Jim Palmer (based on chunking idea from Dave Koelle)
* Contributors: Mike Grier (mgrier.com), Clint Priest, Kyle Adams, guillermo
*/
function naturalSort (a, b) {
var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
sre = /(^[ ]*|[ ]*$)/g,
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
hre = /^0x[0-9a-f]+$/i,
ore = /^0/,
// convert all to strings and trim()
x = a.toString().replace(sre, '') || '',
y = b.toString().replace(sre, '') || '',
// chunk/tokenize
xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
// numeric, hex or date detection
xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null;
// first try and sort Hex codes or Dates
if (yD)
if ( xD < yD ) return -1;
else if ( xD > yD ) return 1;
// natural sorting through split numeric strings and default strings
for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams)
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return (isNaN(oFxNcL)) ? 1 : -1;
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
else if (typeof oFxNcL !== typeof oFyNcL) {
oFxNcL += '';
oFyNcL += '';
}
if (oFxNcL < oFyNcL) return -1;
if (oFxNcL > oFyNcL) return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并应用于:
// Natural Sort (disabled because it is super freaking slow.... need xsl transform sorting instead)
var sortedSet = $(data).children("documents").children("document").sort(function(a, b) {
return naturalSort($(a).children('index').text(), $(b).children('index').text());
});
Run Code Online (Sandbox Code Playgroud)
这在我们的另一个较小的XML文件上工作正常,但是对于巨大的500kb-ish文件Safari(v4)只是暂时挂起几分钟来排序,而Firefox(最新)需要大约10秒来处理(仍然不好,但至少是理智的).
我还发现了另一个叫做Alphanum的更小/更轻的脚本:
function alphanum(a, b) {
function chunkify(t) {
var tz = [], x = 0, y = -1, n = 0, i, j;
while (i = (j = t.charAt(x++)).charCodeAt(0)) {
var m = (i == 46 || (i >=48 && i <= 57));
if (m !== n) {
tz[++y] = "";
n = m;
}
tz[y] += j;
}
return tz;
}
var aa = chunkify(a);
var bb = chunkify(b);
for (x = 0; aa[x] && bb[x]; x++) {
if (aa[x] !== bb[x]) {
var c = Number(aa[x]), d = Number(bb[x]);
if (c == aa[x] && d == bb[x]) {
return c - d;
} else return (aa[x] > bb[x]) ? 1 : -1;
}
}
return aa.length - bb.length;
}
Run Code Online (Sandbox Code Playgroud)
这对于Safari来说运行得更快,但仍然会锁定浏览器一分钟左右.
我做了一些研究,似乎有些人建议使用XSL对XML条目进行排序,这显然要快得多,因为它是内置在浏览器中而不是运行在JavaScript之上.
显然有几种不同的实现,Sarissa多次被提及,sourceforge页面似乎表明最后一次更新发生在2011-06-22.
还有其他选择,例如xslt.js
我的问题是:
谢谢你看我的问题.
好问题,+ 1.
这是一个XSLT 1.0解决方案(有一个XSLT 2.0解决方案更简单,更容易编写,可能更高效,但5个主要浏览器都没有配备XSLT 2.0处理器):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="xml">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vDigits" select="'0123456789'"/>
<xsl:variable name="vPadding" select=
"' '"/>
<xsl:variable name="vMaxNumLength"
select="string-length($vPadding)"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<t>
<xsl:apply-templates/>
</t>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>
<t>
<xsl:for-each select="$vPass1/*/*">
<xsl:sort select="@sortMe"/>
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:for-each>
</t>
</xsl:template>
<xsl:template match="str">
<str>
<xsl:apply-templates select="text()" mode="normalize"/>
<xsl:copy-of select="text()"/>
</str>
</xsl:template>
<xsl:template match="text()" mode="normalize" name="normalize">
<xsl:param name="pText" select="."/>
<xsl:param name="pAccum" select="''"/>
<xsl:choose>
<xsl:when test="not(string-length($pText) >0)">
<xsl:attribute name="sortMe">
<xsl:value-of select="$pAccum"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vChar1" select="substring($pText,1,1)"/>
<xsl:choose>
<xsl:when test="not(contains($vDigits,$vChar1))">
<xsl:variable name="vDig1" select=
"substring(translate($pText,
translate($pText, $vDigits, ''),
''
),
1,1)"/>
<xsl:variable name="vDig">
<xsl:choose>
<xsl:when test="string-length($vDig1)">
<xsl:value-of select="$vDig1"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="vNewText" select=
"substring-before(concat($pText,$vDig), $vDig)"/>
<xsl:call-template name="normalize">
<xsl:with-param name="pText" select=
"substring($pText, string-length($vNewText)+1)"/>
<xsl:with-param name="pAccum" select=
"concat($pAccum, $vNewText)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vNonDig1" select=
"substring(translate($pText, $vDigits, ''),1,1)"/>
<xsl:variable name="vNonDig">
<xsl:choose>
<xsl:when test="string-length($vNonDig1)">
<xsl:value-of select="$vNonDig1"/>
</xsl:when>
<xsl:otherwise>Z</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="vNum" select=
"substring-before(concat($pText,'Z'),$vNonDig)"/>
<xsl:variable name="vNumLength" select=
"string-length($vNum)"/>
<xsl:variable name="vNewText" select=
"concat(substring($vPadding,
1,
$vMaxNumLength -$vNumLength),
$vNum
)"/>
<xsl:call-template name="normalize">
<xsl:with-param name="pText" select=
"substring($pText, $vNumLength +1)"/>
<xsl:with-param name="pAccum" select=
"concat($pAccum, $vNewText)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
当此转换应用于以下XML文档时:
<t>
<str>Allegia 6R Clasteron</str>
<str>200X Radonius</str>
<str>Xiph Xlater 10000</str>
<str>1000X Radonius Maximus</str>
<str>Callisto Morphamax 6000 SE</str>
<str>10X Radonius</str>
<str>20X Radonius</str>
<str>30X Radonius</str>
<str>20X Radonius Prime</str>
<str>40X Radonius</str>
<str>Allegia 50 Clasteron</str>
<str>Allegia 500 Clasteron</str>
<str>Allegia 50B Clasteron</str>
<str>Allegia 51 Clasteron</str>
<str>Alpha 100</str>
<str>Alpha 2</str>
<str>Alpha 200</str>
<str>Alpha 2A</str>
<str>Alpha 2A-8000</str>
<str>Alpha 2A-900</str>
<str>Callisto Morphamax</str>
<str>Callisto Morphamax 500</str>
<str>Callisto Morphamax 5000</str>
<str>Callisto Morphamax 600</str>
<str>Callisto Morphamax 6000 SE2</str>
<str>Callisto Morphamax 700</str>
<str>Callisto Morphamax 7000</str>
<str>Xiph Xlater 2000</str>
<str>Xiph Xlater 300</str>
<str>Xiph Xlater 40</str>
<str>Xiph Xlater 5</str>
<str>Xiph Xlater 50</str>
<str>Xiph Xlater 500</str>
<str>Xiph Xlater 5000</str>
<str>Xiph Xlater 58</str>
</t>
Run Code Online (Sandbox Code Playgroud)
产生了想要的,正确的"自然分类"结果:
<t>
<str>10X Radonius</str>
<str>20X Radonius</str>
<str>20X Radonius Prime</str>
<str>30X Radonius</str>
<str>40X Radonius</str>
<str>200X Radonius</str>
<str>1000X Radonius Maximus</str>
<str>Allegia 6R Clasteron</str>
<str>Allegia 50 Clasteron</str>
<str>Allegia 50B Clasteron</str>
<str>Allegia 51 Clasteron</str>
<str>Allegia 500 Clasteron</str>
<str>Alpha 2</str>
<str>Alpha 2A</str>
<str>Alpha 2A-900</str>
<str>Alpha 2A-8000</str>
<str>Alpha 100</str>
<str>Alpha 200</str>
<str>Callisto Morphamax</str>
<str>Callisto Morphamax 500</str>
<str>Callisto Morphamax 600</str>
<str>Callisto Morphamax 700</str>
<str>Callisto Morphamax 5000</str>
<str>Callisto Morphamax 6000 SE</str>
<str>Callisto Morphamax 6000 SE2</str>
<str>Callisto Morphamax 7000</str>
<str>Xiph Xlater 5</str>
<str>Xiph Xlater 40</str>
<str>Xiph Xlater 50</str>
<str>Xiph Xlater 58</str>
<str>Xiph Xlater 300</str>
<str>Xiph Xlater 500</str>
<str>Xiph Xlater 2000</str>
<str>Xiph Xlater 5000</str>
<str>Xiph Xlater 10000</str>
</t>
Run Code Online (Sandbox Code Playgroud)
重要假设:此解决方案假设任何数字都不会超过40位.虽然在普遍存在的实际情况中会出现这种情况,但如果出现此限制不足的情况,则很容易修改此解决方案以接受限制值作为外部/全局参数.
最后,表现:
处理类似于上面的XML文档,但具有1700个str元素需要0.659秒.在我8岁的奔腾单核3GHz CPU,2GB RAM计算机上.
说明:
这是一个两遍解决方案.
在第一次传递中,所有节点都"按原样"复制,除了将sortMe属性添加到每个str元素.此属性包含唯一的文本节点子节点的字符串值str- 其中任何数字左边用空格填充,总固定长度为40.
在Pass 2中,我们str使用单个排序键(sortMe属性)按字母顺序对所有元素进行排序.
现在,回答所有4个原始问题:
我的问题是:
XSL是这个特定问题的最佳排序选项吗?
如果是这样,我如何使用XSL进行自然排序?(url to resources?)
如果两个问题都是,我应该使用哪个库以获得最佳兼容性和速度?
如果XSL不是最佳选择,那么哪一个?
答案:
任何最佳排序算法的实现(无论语言如何)都应该足够.在这方面,XSLT是一个不错的选择.
上面的代码提供了一个完整而精确的"自然"排序的XSLT实现.
不需要库 - 只需按原样使用上述代码即可.如果您需要有关如何从PL调用转换的帮助,请参阅相应的文档.
包含的任何PL,XSLT以及最佳排序算法的实现都是合适的选择.
| 归档时间: |
|
| 查看次数: |
670 次 |
| 最近记录: |