在不影响其他对象的情况下重新排序对象
reordering objects without impacting other objects
我有一个项目列表(想想目录中的文件),其中这些项目的顺序由用户任意管理。用户可以在其他项之间插入项、删除项和移动项。
将排序存储为每个项的属性,以便在插入或移动特定项时,不影响其他项的排序属性的最佳方法是什么?这些对象将被存储在数据库中。
一个理想的实现应该能够支持无限数量的插入/重排序。
我用来识别这种方法的局限性的测试如下:
有3个项目x,y,z,反复取左边的项目,放在另外两个项目之间;然后把右边的物体放在另外两个物体之间;
为供其他人参考,我已经包含了一些我尝试过的算法
小数、双精度
将顺序存储为小数。若要在两个顺序为x和y的项之间插入一个,则计算其顺序为x/2+y/2。
限制:
精度,或性能。使用双精度,当分母变得太大时,我们最终得到x/2+y/2==x。在Javascript中,它只能处理25次洗牌。
function doubles(x,y,z) {
for (var i = 0; i < 10000000; i++) {
//x,y,z
//x->v1: y,v1,z
//z->v2: y,v2,v1
var v1 = y/2 + z/2
var v2 = y/2 + v1/2
x = y
y = v2
z = v1
if (x == y) {
console.log(i)
break
}
}
}
>doubles(1, 1.5, 2)
>25
1.2。小数,BigDecimal
与上面相同,但是使用了来自https://github.com/iriscouch/bigdecimal.js的BigDecimal。在我的测试中,性能下降得非常快。对于其他框架来说,这可能是一个不错的选择,但对于客户端javascript来说,就不行了。
我扔掉了那个实现,不再拥有它了。
2.1。分数
将顺序存储为(分子,分母)整数元组。要在项目xN/xD和yN/yD之间插入一个项目,给它一个值(xN+yN)/(xD+yD)(它可以很容易地显示在其他两个数字之间)。
限制:
精度或溢出。
function fractions(xN, xD, yN, yD, zN, zD){
for (var i = 0; i < 10000000; i++) {
//x,y,z
//x->v1: y,v1,z
//z->v2: y,v2,v1
var v1N = yN + zN, v1D = yD + zD
var v2N = yN + v1N, v2D = yD + v1D
xN = yN, xD=yD
yN = v2N, yD=v2D
zN = v1N, zd=v1D
if (!isFinite(xN) || !isFinite(xD)) { // overflow
console.log(i)
break
}
if (xN/xD == yN/yD) { //precision
console.log(i)
break
}
}
}
>fractions(1,1,3,2,2,1)
>737
2.2。GCD还原分数
与上面相同,但使用最大公分母算法进行分数化简:
function gcd(x, y) {
if(!isFinite(x) || !isFinite(y)) {
return NaN
}
while (y != 0) {
var z = x % y;
x = y;
y = z;
}
return x;
}
function fractionsGCD(xN, xD, yN, yD, zN, zD) {
for (var i = 0; i < 10000000; i++) {
//x,y,z
//x->v1: y,v1,z
//z->v2: y,v2,v1
var v1N = yN + zN, v1D = yD + zD
var v2N = yN + v1N, v2D = yD + v1D
var v1gcd=gcd(v1N, v1D)
var v2gcd=gcd(v2N, v2D)
xN = yN, xD = yD
yN = v2N/v2gcd, yD=v2D/v2gcd
zN = v1N/v1gcd, zd=v1D/v1gcd
if (!isFinite(xN) || !isFinite(xD)) { // overflow
console.log(i)
break
}
if (xN/xD == yN/yD) { //precision
console.log(i)
break
}
}
}
>fractionsGCD(1,1,3,2,2,1)
>6795
3。字母
使用字母顺序。这个想法是从一个字母表开始(比如,ascii可打印范围[32..126]),然后增加字符串。所以,(O是我们范围的中间),插入到a和c之间,用b,插入到a和b之间,用aO,以此类推。
限制:
字符串将变得太长,以至于无法在数据库中容纳。
function middle(low, high) {
for(var i = 0; i < high.length; i++) {
if (i == low.length) {
//aa vs aaf
lowcode=32
hicode = high.charCodeAt(i)
return low + String.fromCharCode( (hicode - lowcode) / 2)
}
lowcode = low.charCodeAt(i)
hicode = high.charCodeAt(i)
if(lowcode==hicode) {
continue
}
else if(hicode - lowcode == 1) {
// aa vs ab
return low + 'O';
} else {
// aa vs aq
return low.slice(0,i) + String.fromCharCode(lowcode + (hicode - lowcode) / 2)
}
}
}
function alpha(x,y,z, N) {
for (var i = 0; i < 10000; i++) {
//x,y,z
//x->v1: y,v1,z
//z->v2: y,v2,v1
var v1 = middle(y, z)
var v2 = middle(y, v1)
x = y
y = v2
z = v1
if(x.length > N) {
console.log(i)
break
}
}
}
>alpha('?', 'O', '_', 256)
1023
>alpha('?', 'O', '_', 512)
2047
也许我错过了一些基本的东西,我承认我对javascript知之甚少,但肯定你可以实现一个双链表来处理这个?然后重新排序a,b,c,d,e,f,g,h,在d和e之间插入X,你只要解除d->e的链接,链接d->X,然后链接X->e,以此类推。
因为在上面的任何场景中,要么您将耗尽精度(并且您的无限排序丢失),要么您将最终使用非常长的排序标识符并且没有内存:)
软件公理#1:保持简单,直到你找到一个令人信服的、真实的、被证明的理由把它变得更复杂。
所以,我认为当DOM已经为您做这些时,维护您自己的order属性是额外和不必要的代码和维护。为什么不让DOM来维护顺序,这样就可以在需要的时候动态地为当前的顺序生成一组简单的序列号呢?cpu的速度非常快,可以随时为所有项目生成新的序列号,无论您何时需要它或它何时发生变化。并且,如果您想在服务器上保存这个新排序,只需将整个序列发送到服务器。
实现这些分割序列之一,这样你就可以总是插入更多的对象而不必重新编号任何东西,这将是大量的代码和大量的漏洞机会。在证明你确实需要这种程度的复杂性之前,你不应该去那里。
将项目存储在数组中,并使用splice()
插入和删除元素
还是因为你在回应链表答案时所做的评论而不能接受?
你试图解决的问题是潜在的插入排序,它有一个简单的实现O(n^2)。但是有很多方法可以改善它。假设每个元素都有一个顺序变量。你可以巧妙地分配这些顺序,让变量之间有很大的间隙,得到一个平摊的O(n*log(n))机制。看(插入排序是nlogn)
- 如何对两个嵌套对象进行排序
- 按特定顺序对对象进行排序—Angular
- 对包含对象的JSON对象进行排序
- 使用地理坐标,对更靠近用户的对象进行排序'的位置
- 使用多个数组对对象进行排序
- 如何使用父子树对JavaScript对象进行排序
- 如何根据创建日期对对象进行排序,以插入Meteor的高图表
- MongoDB按嵌套对象值排序
- javascript:对具有对象的对象进行排序并读取它们
- 使用原型对对象进行排序
- 在angularjs ng-repeat指令中对对象进行排序
- Javascript使用并行数组对对象进行排序
- AngularJS使用对象属性排序多个对象数组
- 对JSON数组对象进行排序
- 使用javascript/JQM对JSON对象进行排序并附加到html中
- 对画布上的对象进行排序
- 如何在JavaScript中使用主节点文本对json对象进行排序
- 按字段值作为参数对数组中的对象进行排序
- 对JavaScript集合中的对象进行排序
- 如何根据不同排序顺序的多个值对json对象进行排序