Using swift built in partition to manage elements in an array

iOS 14, Swift 5.x

I watched this excellent WWDC from 2018

https://developer.apple.com/videos/play/wwdc2018/223/

And I wrote a shapes editor... and have been trying to use partition as Dave in the video says you should. I got the first three to work, but the last one I had to use a loop- cannot for the life of me figure out how to get it to work with partition.

Can someone see how I might do this?

The first method moves the selected object to the end of the list, works perfectly.

func bringToFrontEA() {
let subset = objects.partition(by: { $0.selected })
let selected = objects[subset...]
let unselected = objects[..<subset]
let reordered = unselected + selected
objects = Array(reordered)
}

The second method moves the selected object to the front of the list. Works prefectly.

func sendToBackEA() {
let subset = objects.partition(by: { !$0.selected })
let selected = objects[subset...]
let unselected = objects[..<subset]
let reordered = unselected + selected
objects = Array(reordered)
}

The third method moves the element just one element back in the list. Works perfectly.

func sendBackEA() {
if let i = objects.firstIndex(where: { $0.selected }) {
  if i == 0 { return }
  let predecessor = i - 1
  let shapes = objects[predecessor...].partition(by: { !$0.selected })
  let slice = objects[predecessor...]
  let row = objects[..<predecessor]
  
  let selected = Array(slice[..<shapes])
  let unselected = Array(slice[shapes...])
  
  objects = row + selected + unselected
}
}

The last method moves the element forward in the list, works perfectly... but unlike the other methods it will not scale as described in the WWDC video.

func bringForwardEA() {
let indexes = objects.enumerated().filter { $0.element.selected == true }.map{$0.offset}
for i in indexes {
    if objects[i+1].unused {
      return
    }
    objects.swapAt(i+1, i)
  }
}

Objects is an array of shapes with a property indicating if it is selected or not. I want to exchange the loop in the last method by using a partition as I did in the first three. It needs to work for one or more selected shapes.

1 answer

  • answered 2021-10-22 19:55 Sweeper

    Looking at the WWDC video, it appears that what you are calling sendBackEA is what WWDC calls bringForward, and what you are calling bringForwardEA is what WWDC calls sendBack.

    Just like how you move the first selected element forward one index (index decreases) in sendBackEA, then move all the other selected elements to immediately after that first selected element. bringForwardEA should do the reverse: move the last selected element backward one index (index increases), then move all the other selected elements to immediately before the last selected element. (See circa 19:10 in the video)

    You seem to have confused yourself by trying to increase the indices of all the selected index by 1. This obviously cannot be done with a partition in general.

    Also note that partition(by:) already modifies the collection, you don't need to get each partition, then recombine.

    Your 4 methods can be written like this:

    func bringToFrontEA() {
        objects.partition(by: { $0.selected })
    }
    
    func sendToBackEA() {
        objects.partition(by: { !$0.selected })
    }
    
    func sendBackEA() {
        if let i = objects.indices.first(where: { objects[$0].selected }) {
          if i == 0 { return }
          let predecessor = i - 1
          objects[predecessor...].partition(by: { !$0.selected })
        }
    }
    
    func bringForwardEA() {
        if let i = objects.indices.last(where: { objects[$0].selected }) {
            if i == objects.indices.last { return }
            let successor = i + 1
            objects[...successor].partition(by: { !$0.selected })
        }
    }
    

    Notice the symmetry between sendBackEA and bringForwardEA.

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum