【问题标题】:How to rearrange item of an array to new position in Swift?如何在 Swift 中将数组的项目重新排列到新位置?
【发布时间】:2016-04-11 06:54:33
【问题描述】:

考虑数组[1,2,3,4]。如何将数组项重新排列到新位置。

例如:

put 3 into position 4 [1,2,4,3]

put 4 in to position 1 [4,1,2,3]

put 2 into position 3 [1,3,2,4].

【问题讨论】:

    标签: arrays swift


    【解决方案1】:

    Swift 3.0+:

    let element = arr.remove(at: 3)
    arr.insert(element, at: 2)
    

    并以函数形式:

    func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
        var arr = array
        let element = arr.remove(at: fromIndex)
        arr.insert(element, at: toIndex)
    
        return arr
    }
    

    斯威夫特 2.0:

    这会将 3 置于位置 4。

    let element = arr.removeAtIndex(3)
    arr.insert(element, atIndex: 2)
    

    你甚至可以做一个通用函数:

    func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
        var arr = array
        let element = arr.removeAtIndex(fromIndex)
        arr.insert(element, atIndex: toIndex)
    
        return arr
    }
    

    这里需要vararr,因为如果不将输入参数指定为in-out,就无法改变它。然而,在我们的例子中,我们得到了一个没有副作用的纯函数,在我看来,这更容易推理。 然后你可以这样称呼它:

    let arr = [1,2,3,4]
    rearrange(arr, fromIndex: 2, toIndex: 0) //[3,1,2,4]
    

    【讨论】:

    • swift 能有效处理这种删除和插入吗?
    • 在大多数情况下,是的,如果您经常这样做,可能会给您带来一些问题,但我不会担心,除非它是您应用程序中的真正瓶颈 :)
    • 最后一行不应该读 fromIndex:2 吗? 3 是第二个索引处的第三个元素
    • Swift 3.0+ 解决方案中有一个错字:您将 3 和 2 硬编码在正文中:)
    • 对于toIndex大于fromIndex的情况,这里的解决方案是否存在缺陷,因为removeAtIndex会改变目标toIndex点的索引?
    【解决方案2】:

    所有的好答案!这是一个更完整的 Swift 5 解决方案,它考虑到了性能并为基准和 GIF 爱好者提供了奖励。 ✌️

    extension Array where Element: Equatable
    {
        mutating func move(_ element: Element, to newIndex: Index) {
            if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
        }
    }
    
    extension Array
    {
        mutating func move(from oldIndex: Index, to newIndex: Index) {
            // Don't work for free and use swap when indices are next to each other - this
            // won't rebuild array and will be super efficient.
            if oldIndex == newIndex { return }
            if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
            self.insert(self.remove(at: oldIndex), at: newIndex)
        }
    }
    

    【讨论】:

    • 文档说:“使用与 i 和 j 相同的索引调用 swapAt(::) 无效。”我认为这意味着这里不需要if oldIndex == newIndex { return } 行。编辑:好吧,我又想了。如果你保留这条线,abs(newIndex - oldIndex) 不会被不必要地计算。
    • 有人能解释一下为什么不在所有情况下都使用swapAt,而不仅仅是在它们并排时使用。对于这些情况,使用insert/remove 是更好的解决方案吗?
    • 考虑[A, B, C, D]。将索引3 处的项目移动到0 会得到[D, A, B, C]。将索引 3 处的项目交换为 0 会得到 [D, B, C, A]
    • 谢谢,这解释了它所以交换“改变”整个数组而不是两个位置。
    • @VladimirAmiorkov:顾名思义,swapAt 交换了两个元素。这相当于同时删除元素 A 和 B,然后在 B 之前的位置插入 A,在交换之前 A 的位置插入 B。 [w, A, x, y, B, z].swapAt(1, 4) 将导致 [w, B, x, y, A, z],A 和 B 的位置交换,其他元素(w、x、y 和 z)的位置不变。
    【解决方案3】:

    编辑/更新:Swift 3.x

    extension RangeReplaceableCollection where Indices: Equatable {
        mutating func rearrange(from: Index, to: Index) {
            precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indices")
            insert(remove(at: from), at: to)
        }
    }
    

    var numbers = [1,2,3,4]
    numbers.rearrange(from: 1, to: 2)
    
    print(numbers)  // [1, 3, 2, 4]
    

    【讨论】:

    • 不错,对于 Swift 3:扩展数组 { mutating func rerange(from: Int, to: Int) { insert(remove(at: from), at: to) } } var myArray = [1 ,2,3,4] myArray.rearrange(from: 1, to: 2) print(myArray)
    • insert(remove...) 如果元素不存在怎么办?
    • 有一个前提条件
    • 对于toIndex大于fromIndex的情况,这里的解决方案是否存在缺陷,因为removeAtIndex会改变目标toIndex点的索引?
    • Leo,在我看来,删除操作会产生副作用,这会影响函数的工作方式,具体取决于您是向前还是向后移动项目。然而,这似乎是我错了。
    【解决方案4】:

    来自 Leo 的好建议。

    对于 Swift 3...5.5:

    extension Array {  
        mutating func rearrange(from: Int, to: Int) {
            insert(remove(at: from), at: to)
        }
    }
    
    var myArray = [1,2,3,4]
    myArray.rearrange(from: 1, to: 2)   
    print(myArray)
    

    【讨论】:

      【解决方案5】:
      var arr = ["one", "two", "three", "four", "five"]
      
      // Swap elements at index: 2 and 3
      print(arr)
      arr.swapAt(2, 3)
      print(arr)
      

      【讨论】:

      • 简短而甜蜜的回答!
      【解决方案6】:

      斯威夫特 4.2

      extension Array where Element: Equatable {
          mutating func move(_ item: Element, to newIndex: Index) {
              if let index = index(of: item) {
                  move(at: index, to: newIndex)
              }
          }
      
          mutating func bringToFront(item: Element) {
              move(item, to: 0)
          }
      
          mutating func sendToBack(item: Element) {
              move(item, to: endIndex-1)
          }
      }
      
      extension Array {
          mutating func move(at index: Index, to newIndex: Index) {
              insert(remove(at: index), at: newIndex)
          }
      }
      

      【讨论】:

        【解决方案7】:

        我们可以使用 swap 方法来交换数组中的元素:

        var arr = ["one", "two", "three", "four", "five"]
        
        // Swap elements at index: 2 and 3
        print(arr)
        swap(&arr[2], &arr[3])
        print(arr)
        

        【讨论】:

        • 我的问题是如何重新排列项目而不是交换 2 个项目。
        • 两者都与您想将项目从一个索引移动到另一个索引相同,我猜交换是​​一种更好的方式。
        • 嗯。 [1,2,3,4,5] 交换 3 和 5 将有 [1,2,5,4,3] 但我想要的是 [1,2,4,5,3] 这是在索引 2 处移动项目进入索引 4
        • 那么它很简单,就像:let element = arr.removeAtIndex(2) & arr.append(element)
        【解决方案8】:

        @ian 提供了很好的解决方案,但是当数组越界添加检查时它也会崩溃

        extension Array where Element: Equatable {
            public mutating func move(_ element: Element, to newIndex: Index) {
                if let oldIndex: Int = index(of: element) {
                    self.move(from: oldIndex, to: newIndex)
                }
            }
        
            public mutating func moveToFirst(item: Element) {
                self.move(item, to: 0)
            }
        
            public mutating func move(from oldIndex: Index, to newIndex: Index) {
                // won't rebuild array and will be super efficient.
                if oldIndex == newIndex { return }
                // Index out of bound handle here
                if newIndex >= self.count { return }
                // Don't work for free and use swap when indices are next to each other - this
                if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
                // Remove at old index and insert at new location
                self.insert(self.remove(at: oldIndex), at: newIndex)
            }
        }
        

        【讨论】:

          【解决方案9】:

          swift 中没有数组的移动功能。您可以通过从那里删除对象来获取索引中的对象,然后使用“插入”将其放入您最喜欢的索引中

          var swiftarray = [1,2,3,4]
          let myobject = swiftarray.removeAtIndex(1) // 2 is the object at 1st index
          let myindex = 3
          swiftarray.insert(myobject, atIndex: myindex) // if you want to insert the    object to a particular index here it is 3
          swiftarray.append(myobject) // if you want to move the object to last index
          

          【讨论】:

            【解决方案10】:

            Swift 4 - 将一组项目从IndexSet 的索引中移动、分组并将它们移动到目标索引的解决方案。通过扩展至RangeReplaceableCollection 实现。包括删除和返回IndexSet 中所有项目的方法。我不确定如何将扩展限制为更通用的形式,而不是限制元素而不是整数,同时保持构造 IndexSets 的能力,因为我对 Swift 协议的了解并不广泛。

            extension RangeReplaceableCollection where Self.Indices.Element == Int {
            
                /**
                    Removes the items contained in an `IndexSet` from the collection.
                    Items outside of the collection range will be ignored.
            
                    - Parameter indexSet: The set of indices to be removed.
                    - Returns: Returns the removed items as an `Array<Self.Element>`.
                */
                @discardableResult
                mutating func removeItems(in indexSet: IndexSet) -> [Self.Element] {
            
                    var returnItems = [Self.Element]()
            
                    for (index, _) in self.enumerated().reversed() {
                        if indexSet.contains(index) {
                            returnItems.insert(self.remove(at: index), at: startIndex)
                        }
                    }
                    return returnItems
                }
            
            
                /**
                    Moves a set of items with indices contained in an `IndexSet` to a     
                    destination index within the collection.
            
                    - Parameters:
                        - indexSet: The `IndexSet` of items to move.
                        - destinationIndex: The destination index to which to move the items.
                    - Returns: `true` if the operation completes successfully else `false`.
            
                    If any items fall outside of the range of the collection this function 
                    will fail with a fatal error.
                */
                @discardableResult
                mutating func moveItems(from indexSet: IndexSet, to destinationIndex: Index) -> Bool {
            
                    guard indexSet.isSubset(of: IndexSet(indices)) else {
                        debugPrint("Source indices out of range.")
                        return false
                        }
                    guard (0..<self.count + indexSet.count).contains(destinationIndex) else {
                        debugPrint("Destination index out of range.")
                        return false
                    }
            
                    let itemsToMove = self.removeItems(in: indexSet)
            
                    let modifiedDestinationIndex:Int = {
                        return destinationIndex - indexSet.filter { destinationIndex > $0 }.count
                    }()
            
                    self.insert(contentsOf: itemsToMove, at: modifiedDestinationIndex)
            
                    return true
                }
            }
            

            【讨论】:

              【解决方案11】:

              这是一个解决方案,其中包含就地更改数组和返回已更改数组的函数:

              extension Array {
                  func rearranged(from fromIndex: Int, to toIndex: Int) -> [Element] {
                      var arr = self
                      let element = arr.remove(at: fromIndex)
                      
                      if toIndex >= self.count {
                          arr.append(element)
                      } else {
                          arr.insert(element, at: toIndex)
                      }
                      return arr
                  }
                  
                  mutating func rearrange(from fromIndex: Int, to toIndex: Int) {
                      let element = self.remove(at: fromIndex)
                      if toIndex >= self.count {
                          self.append(element)
                      } else {
                          self.insert(element, at: toIndex)
                      }
                  }
              }
              

              【讨论】:

              • self.remove() 将更改数组,因此toIndex 不一定会指向与调用rearrange() 之前相同的项目。取决于fromIndex 是大于还是小于toIndex
              【解决方案12】:

              使用 Swift 4 更新, 滑动数组索引

              for (index,addres) in self.address.enumerated() {
                   if addres.defaultShipping == true{
                        let defaultShipping = self.address.remove(at: index)
                        self.address.insert(defaultShipping, at: 0)
                   }
              }
              

              【讨论】:

                【解决方案13】:

                高效的解决方案:

                extension Array 
                {
                    mutating func move(from sourceIndex: Int, to destinationIndex: Int)
                    {
                        guard
                            sourceIndex != destinationIndex
                            && Swift.min(sourceIndex, destinationIndex) >= 0
                            && Swift.max(sourceIndex, destinationIndex) < count
                        else {
                            return
                        }
                
                        let direction = sourceIndex < destinationIndex ? 1 : -1
                        var sourceIndex = sourceIndex
                
                        repeat {
                            let nextSourceIndex = sourceIndex + direction
                            swapAt(sourceIndex, nextSourceIndex)
                            sourceIndex = nextSourceIndex
                        }
                        while sourceIndex != destinationIndex
                    }
                }
                

                【讨论】:

                  【解决方案14】:
                  func adjustIndex(_ index: Int, forRemovalAt removed: Int) -> Int {
                      return index <= removed ? index : index - 1
                  }
                  
                  extension Array
                  {
                      mutating func move(from oldIndex: Index, to newIndex: Index) {
                          insert(remove(at: oldIndex), at: adjustIndex(newIndex, forRemovalAt: oldIndex))
                      }
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    Leo Dabus 的解决方案很棒,但是如果不满足条件,使用前置条件(从 != 到 && 索引.contains(从 != 到 && 索引.contains(to),“无效索引”)将使应用程序崩溃。我将其更改为保护和 if 语句 - 如果由于某种原因不满足条件,则不会发生任何事情并且应用程序会继续运行。我认为我们应该避免进行可能导致应用程序崩溃的扩展。如果您希望可以使重新排列功能返回a Bool - 如果成功则为 true,如果失败则为 false。 更安全的解决方案:

                    extension Array {
                    mutating func rearrange(from: Int, to: Int) {
                        guard from != to else { return }
                        //precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indexes")
                        if indices.contains(from) && indices.contains(to) {
                            insert(remove(at: from), at: to)
                        }
                    }
                    

                    【讨论】:

                    • 请扩展您的答案。为什么这样更安全?
                    • Leo,感谢您提供的链接,非常有用。我承认没有完全理解前提条件和断言的使用,并且链接为我澄清了其中的一些内容。为了我自己的使用,即使数组不包含索引,我也需要重新排列函数以不使应用程序崩溃,而只是保持数组不变。
                    【解决方案16】:

                    功能(不是快速但通用..查找/删除/插入):

                    func c_move_to(var array:Array,var from:Int,var to:Int):
                    
                        var val = array[from]
                        array.remove(from)
                        array.insert(to,val)
                        return array
                    

                    使用方法:

                    print("MOVE 0 to 3  [1,2,3,4,5]"  , c_move_to([1,2,3,4,5],0,3))
                    print("MOVE 1 to 2  [1,2,3,4,5]"  , c_move_to([1,2,3,4,5],1,2)) 
                    

                    吐出:

                    MOVE 0 to 3  [1,2,3,4,5][2, 3, 4, 1, 5]
                    MOVE 1 to 2  [1,2,3,4,5][1, 3, 2, 4, 5]
                    

                    【讨论】:

                    • 你说的是通用的,不是快速的。那为什么要发帖?不回答OP的问题。已经发布了更好的 Swift 答案 (array.swap(...))
                    【解决方案17】:

                    这个解决方案怎么样? 要更改的元素和要更改的元素已更改。

                    // Extenstion
                    
                    extension Array where Element: Equatable {
                      mutating func change(_ element: Element, to newIndex: Index) {
                        if let firstIndex = self.firstIndex(of: element) {
                          self.insert(element, at: 0)
                          self.remove(at: firstIndex + 1)
                        }
                      }
                    }
                    
                    // Example
                    
                    var testArray = ["a", "b", "c", "EE", "d"]
                    testArray.change("EE", to: 0)
                    
                    // --> Result
                    // ["EE", "a", "b", "c", "d"]
                    

                    【讨论】:

                    • 对不起,这个答案不正确。 2 个问题 -> 您没有使用“newIndex”变量;您需要在插入之前删除元素 -> 这是因为 newindex 可以在现有索引之前或之后(当您这样做时 (firstindex +1) 您假设 newIndex
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-12-13
                    • 1970-01-01
                    • 2019-04-06
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-03-13
                    相关资源
                    最近更新 更多