BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

SOLVED: Need to create and maintain a list of 'unused' numbers

Forums > SwiftUI

I need to create and maintain a list of ‘used numbers’ every year which are also uniquely associated with a colour. I’m thinking I will need to store this in its own CoreData entity, but I’m trying to model the entity using structs so that I can work out the interface. I’ve been doing this over a large app already.

I would like help knowing should this be an array? A set? And the intricacies of getting an NSSet to an array from CoreData, plus I get muddled up on Getters and Setters in Swift, when they are appropriate. I also struggle with optional Dates and presenting them to a user.

So far I have this as my struct:

import Foundation
import SwiftUI

struct usedSprayColourNumbersByYear: Identifiable, Equatable {
var id: String
var year: Date = .now
 var colour: SprayColoursEnum
    var usedNumbers = [Int]()
     }

the enum:

enum SprayColoursEnum: String, CaseIterable {
 case red, green, purple, orange, black, blue, white

   var colourValue: UIColor {

      get {

          switch self {
            case .red: return UIColor.red
            case .green: return UIColor.green
            case .purple: return UIColor.purple
            case .orange: return UIColor.orange
            case .black: return UIColor.black
            case .blue: return UIColor.blue
            case .white: return UIColor.white
         }
     }
 }
}

The numbers must be unique within colour and year. When the user needs an unused number for a given colour and year then the app must offer the lowest unused number that is available for them to use. So it can be saved in the used number list.

Sometimes the user might accidentally use a much larger number than the lowest available and they must add this number to the used number list. For example, Orange 78 might be the next highest number, but the user might accidentally need to record that they used Orange 87. So the Orange sequence available will subsequently be Orange - 78, 79, 80, 81, 82, 83, 84, 85, 86, 88… etc.

The interface looks like this so far - The user picks their spray colour. Next they should be presented with this list of unused numbers. https://drive.google.com/file/d/1B1bOPjHFNJXVpCqUBJid-NEFNflJNPMa/view?raw=1

spray can

Happy to include any other code if it helps.

Thanks,

Vickie

2      

(Not sure how to embed an image or gif in this forum, but happy to add if instructed! Sorry!)

  1. Upload the image to something like Dropbox or Imgur or whatever image hosting site you like.
  2. Post a URL to the image using this markup:
![image description](image URL)

Note: If you use Dropbox, you can change the dl=0 on the end of the URL to raw=1 to make it show up as an embedded image rather than a link. I don't know if other services have a similar feature, but I bet they do.

Example: This link:

![business cat](https://www.dropbox.com/s/z5n17b3y72zcmx1/fire_the_dogs_sm.png?raw=1)

results in this image:

business cat

3      

Thanks so much, @roosterboy.

Do you think the list of unused numbers should be a set or an array of ints? or?

Thanks,

Vickie

2      

Definitely a set to store the list of used numbers.

And from there it would be easy enough to determine what the next available number should be, even with gaps in the sequence. When I'm back at my computer later, I can post some example code if you need some pointing in the right direction.

3      

Thank you so much, that would be brilliant

2      

Whilst waiting for @roosterboy's code, here is some code that you can put into the playground and see the effect.

var orange: Set = [1,2,3,4,5,6,7,8,9,10]
print(orange)   // a set is unordered so the numbers are usually not printed in a sequence
orange.remove(4)   // remove number 4 from the set
print(orange)

var orangeArray = orange.sorted()
print(orangeArray)   // This is the set converted to a sorted array

orange.remove(1)
print(orange.first!)  // This is not necessarily the same as the first element above in 'orange'

orange.remove(2)
orange.remove(5)
orangeArray = orange.sorted()
print(orange)
print(orangeArray)

let itemToRemove = orangeArray.first
orange.remove(itemToRemove!)
print(orange)

if !orange.isEmpty {                  // as long as there is something in the set
    orange.remove(orange.sorted().first!)    // remove the first element, after the set has been sorted
}
print(orange)
print(orange.sorted())

3      

Without knowing exactly how these are being used in your code (e.g., in a picker, in a freeform text field, etc), it's hard to be more specific in my example, but the general principle you would follow can be illustrated as below:

/*
 The problem: Given a set of used numbers, possibly not contiguous, 
 how to determine the lowest next available number?
 */

import Foundation

//our set of already used numbers
//note there are some gaps in the sequence
let usedNumbers: Set<Int> = [1,2,3,4,5,6,7,9,11,12,13,27,28]

//to find the lowest next available number, taking any gaps
// into account, we will create a new set encompassing a full
// range from the lowest used number to the highest used number,
// regardless of whether each number has been used or not, and
// then subtract the numbers that have actually been used
//this will result in a set of the numbers from any gaps in the
// sequence of used numbers, or an empty set (if there are no gaps)
let availableNumbers = Set(usedNumbers.min()!...usedNumbers.max()!)
                            .subtracting(usedNumbers)

//now we can calculate the next available number
let nextNumber: Int
if availableNumbers.isEmpty {
    //if our set of available numbers is empty, that means
    // there were no gaps in the used numbers and we can
    // just use the highest used number + 1
    nextNumber = usedNumbers.max()! + 1
} else {
    //if our set of available numbers is not empty
    // then there was at least one gap in the used 
    // numbers and we want the lowest number from 
    // the gap
    nextNumber = availableNumbers.min()!
}

print(nextNumber)

Make sense?

3      

Thank you both so much.

Just for info, I thought I'd present finished code before marking this as solved. However, as @roosterboy said, "Without knowing exactly how these are being used in the code (e.g., in a picker, in a freeform text field, etc)" is now the reason it's taking me longer to answer.

I'll give it one more day then just mark as solved. Maybe my next steps will become a next question?

2      

For all concerned - both @roosterboy's and @Greenamberred's solutions were useful.

2      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.