Swift 将CollectionDifference应用于NSTableView

Swift 将CollectionDifference应用于NSTableView,swift,macos,cocoa,Swift,Macos,Cocoa,对于本例,假设我从annInt数组生成一个CollectionDifference,然后像这样调用inferringMoves a=[18,18,19,11] 设b=[11,19] 设diff=b.difference(from:a).inferringMoves() 对于差异的变化{ 开关变换{ 案例let.insert(偏移量,u,关联): 如果let from=关联{ 打印(“移动”、起始、偏移) }否则{ 打印(“插入”,偏移) } 案例let.remove(偏移量,u,关联): //如

对于本例,假设我从ann
Int
数组生成一个
CollectionDifference
,然后像这样调用
inferringMoves

a=[18,18,19,11]
设b=[11,19]
设diff=b.difference(from:a).inferringMoves()
对于差异的变化{
开关变换{
案例let.insert(偏移量,u,关联):
如果let from=关联{
打印(“移动”、起始、偏移)
}否则{
打印(“插入”,偏移)
}
案例let.remove(偏移量,u,关联):
//如果是移动,则已记录在中。插入
如果关联==nil{
打印(“删除”,偏移)
}
}
}
现在我需要获取changes数组并将其提供给
nstableview
update方法

  • insertRows
  • dows
  • moveRow
以这样一种方式,它可以干净地应用。我这里的问题是
move
条目的偏移量。上面的代码段生成:

REMOVE 1
REMOVE 0
MOVE 2 1
remove(offset: 2, element: 19, associatedWith: 1)
remove(offset: 1, element: 18, associatedWith: -)
remove(offset: 0, element: 18, associatedWith: -)
insert(offset: 1, element: 19, associatedWith: 2)
[18, 18, 19, 11]
remove(offset: 1, element: 18, associatedWith: -) => [18, 19, 11]
remove(offset: 0, element: 18, associatedWith: -) => [19, 11]
insert(offset: 2, element: 19, associatedWith: 0) => [11, 19]
    a move: insert at offset, remove at associatedWith
现在很明显,我不能为
0
1
调用
RemoveOWS
,然后调用
moveRow(2,1)
,但这就是差异的建议

我怎样才能干净地使用它


问题似乎是
NSTableView在应用插入/删除操作时会立即更新其内部计数,因此移动将不起作用。

简短回答:

inferringMoves()
不会做您认为它会做的事情。仔细查看结果,特别是与
相关的
值,并开发一种算法,该算法实际生成所需的删除、插入和移动–
inferringMoves()
实际上不会生成任何移动

长答案:

你的问题引起了我的兴趣,因为我以前从来没有看过
CollectionDifference
,所以我会去看看。第一步是在互联网上搜索,这会找到苹果的文档(和往常一样糟糕,它是为那些已经知道语义的人编写的,为什么他们不能再提供像样的文档了——大部分像样的东西都在他们的“档案”中,但我有分歧……)和一些描述该功能的网站,包括示例代码。该示例代码中有相当一部分与您的代码不同,但也不必为此感到难过,因为它也不起作用

为什么这么长时间的闲逛?发现没有工作代码让人怀疑你是否患有“锁定热”,你的大脑是否混乱。所有的代码真的不起作用吗?它适用于某些数据集,但不适用于一般情况,苹果称beast
inferringMoves
有点不正确,它推断出序列中的移除和插入操作对,这些操作一起具有移动项目的效果,但实际上并没有推断出单个移动操作

或者我可能(比平时)更糊涂的大脑是这么说的。继续读下去,决定我是否有闭锁症

让我们看看您的数据,看看
差异产生了什么,以及每个步骤如何更改输入:

Input: [18, 18, 19, 11]

Sequence of changes from `difference` and the changing sequence:
    remove(offset: 2, element: 19, associatedWith: -) => [18, 18, 11]
    remove(offset: 1, element: 18, associatedWith: -) => [18, 11]
    remove(offset: 0, element: 18, associatedWith: -) => [11]
    insert(offset: 1, element: 19, associatedWith: -) => [11, 19] CORRECT
在这个序列中重要的是,任何步骤的
偏移量
都考虑了前面的所有步骤,也就是说,它是中间结果的偏移量

现在
inferringMoves
设置与
关联的
字段,以指示构成移动的
移除/插入
对,将其应用于数据的
差异

REMOVE 1
REMOVE 0
MOVE 2 1
remove(offset: 2, element: 19, associatedWith: 1)
remove(offset: 1, element: 18, associatedWith: -)
remove(offset: 0, element: 18, associatedWith: -)
insert(offset: 1, element: 19, associatedWith: 2)
[18, 18, 19, 11]
remove(offset: 1, element: 18, associatedWith: -) => [18, 19, 11]
remove(offset: 0, element: 18, associatedWith: -) => [19, 11]
insert(offset: 2, element: 19, associatedWith: 0) => [11, 19]
    a move: insert at offset, remove at associatedWith
因此,第一个和最后一个动作被推断为移动对

您决定,并且不是唯一一个做出该决定的人,插入操作是应该执行移动的时间,让我们看看会发生什么:

[18, 18, 19, 11]
remove(offset: 2, element: 19, associatedWith: 1) => [18, 18, 19, 11]
    Noop as part of a move pair
remove(offset: 1, element: 18, associatedWith: -) => [18, 19, 11]
    Item 1 was 18 so this seems valid...
remove(offset: 0, element: 18, associatedWith: -) => [19, 11]
    Item 0 is not 18 so this looks like things are going wrong
insert(offset: 1, element: 19, associatedWith: 2) => Oops
    Second action of a move pair, Error item 1 is not 19 and there is no item 2
正如你在医院发现的那样,这不管用。互联网上的其他人认为这是一个移动的移动,他们有更好的表现吗

[18, 18, 19, 11]
remove(offset: 2, element: 19, associatedWith: 1) => [18, 19, 18, 11]
    First of pair, do the move
remove(offset: 1, element: 18, associatedWith: -) => [18, 18, 11]
    Warning bell the item removed is 19 not 18 as the action expects
remove(offset: 0, element: 18, associatedWith: -) => [18, 11]
    Yah, item 0 is 18, this action is "correct" in isolation
insert(offset: 1, element: 19, associatedWith: 2) => [18, 11]
    Second of pair, so NOOP
这也不起作用,所以不要为你的代码不起作用而感到难过,因为我还没有在互联网上找到任何起作用的代码(这并不是说没有任何代码),出错的情况很常见,部分原因可能是许多简单的例子都是偶然发生的

解决这个问题的关键是要弄清楚(苹果没有明确说明)与
值关联的
是一个索引,该索引将(用于将来的插入)或(用于过去的删除)在关联操作发生/发生时存在/存在的序列中受影响的索引

例如,数据的第一个操作是
删除(偏移量:2,元素:19,关联:1)
,这并不意味着您可以将项目移动到当前序列中的索引1,而是移动到序列中的索引1,因为执行关联的
插入(偏移量:1,元素:19,关联:2)
。在对的移除和插入之间有两个中间移除操作,因此顺序将发生变化

要获得(而不是唯一的)工作解决方案,您可以使用以下略图算法对
inferringMoves()
的结果进行后期处理:

  • 删除任何与
  • 值关联的
    移除操作,并在所有后续操作中调整
    偏移量
    值,以允许要移除的元素仍在序列中;及
  • 将与
  • 关联的
    值调整为未通过已删除的成对删除操作删除的元素的当前偏移量
    这将产生一系列零或多个移除和插入操作,这些操作没有与
    值关联的
    ,以及一个或多个插入操作与表示移动的
    值关联的

    将上述算法草图的实现应用于数据会产生:

    REMOVE 1
    REMOVE 0
    MOVE 2 1
    
    remove(offset: 2, element: 19, associatedWith: 1)
    remove(offset: 1, element: 18, associatedWith: -)
    remove(offset: 0, element: 18, associatedWith: -)
    insert(offset: 1, element: 19, associatedWith: 2)
    
    [18, 18, 19, 11]
    remove(offset: 1, element: 18, associatedWith: -) => [18, 19, 11]
    remove(offset: 0, element: 18, associatedWith: -) => [19, 11]
    insert(offset: 2, element: 19, associatedWith: 0) => [11, 19]
        a move: insert at offset, remove at associatedWith
    
    实现这个或另一个算法,就像不做代码编写服务一样,是留给您的。希望上面的解释有意义!如果你被你的工具卡住了