how to loop through array(s) of optionals

This comes up a lot (for years) and has me buggered.


  1. class Act: Codable {
  2. var a: Uint32
  3. var b: [UInt32]?
  4. .
  5. .
  6. .
  7. var z: [UInt32]?
  8. init( ...vars...)
  9. }


I want to do a simple sum on .nonzerobitCount for every var a through z.


I can write code to check,e.g.,


10. if Act.b != nil {for index in 0..<Act.b!.count { if Act.b?[index] != nil { totalBits += Act.b?[index].nonzeroBiCount } } }


but even with copy and paste this is exhausting and cludgy.

Accepted Reply

I don't understand your points:


I tried using optional arrays of optionals. It just made my code messy. It was easier to deal with a formed array that had a zero value than add a couple hundred "!" to optionals calls.


I just proposed to REMOVE optionals


btw: the code "var aToz : [[UInt32]] = []" may work outside a class declaration, as in a playground, but fails within one.


I just tested 1 minute ago, in a class or inside a func, it works perfectly OK of course.

But you need to initialize its content somewhere.

What is the error you get to understand the error you made ?


Anyway, it is good to know it works, don't forget to close the thread.

Replies

All var are arrays, except a. Is it on purpose ?


I want to do a simple sum on .nonzerobitCount for every var a through z.

What do you want to count exactly

Is it ?

        var totalBits = 0
        if b != nil {
            for index in 0 ..< b!.count {
                if b?[index] != nil {
                    totalBits += b?[index].nonzeroBitCount
                }
            }
        }
        if c != nil {
            for index in 0 ..< c!.count {
                if c?[index] != nil {
                    totalBits += c?[index].nonzeroBitCount
                }
            }
        }
     // repeat until z


line 04:

- b is not nil so b? is useless, use b!

- how could b![index] be nil ?

line 05:

- you had a typo : nonzeroBiCount instead of nonzeroBitCount


I would do the following


        var totalBits = 0
        var b: [UInt32]?
        var c: [UInt32]?
        var d: [UInt32]?
        var z: [UInt32]?
        var bToz : [[UInt32]?] = [b, c, d, z]     // You have to type all var names
       
        for xx in bToz {
            if xx != nil {
                for index in 0 ..< xx!.count {
                    totalBits += xx![index].nonzeroBitCount
                }
            }
        }


Hope I understood your request

First of all, you should better re-consider if you really need Optional Arrays.

But that's another issue and you may need it in some cases.


If I were given an Optional Array, I would write something like this:

    var totalBits: Int = 0
    for element in act.b ?? [] {
        totalBits += element.nonzeroBitCount
    }


Or, you can write it in a single line if you are familiar with `reduce`:

    let totalBits = act.b?.reduce(0, {$0 + $1.nonzeroBitCount}) ?? 0


When multiple Optional Arrays given, this would work:

    let totalBits = [act.b ?? [], /* ...,*/ act.z ?? []].joined().reduce(0, {$0 + $1.nonzeroBitCount})

Some refinement to simplify:


       for xx in bToz {
            if xx != nil {
                for item in  xx! {
                    totalBits += item.nonzeroBitCount
                }
            }
        }


or


for xx in bToz where xx != nil {
    totalBits += xx!.reduce(0) { $0 + $1.nonzeroBitCount }
}

in order of response:

to Claude31:

line 04:
- b is not nil so b? is useless, use b!
- how could b![index] be nil ?

It might be that not all b are occupied, e.g., just the first in the array. My fault for not specifying every detail: towit, e.g., b can have up to four members in the array, or none.

line 05:
- you had a typo : nonzeroBiCount instead of nonzeroBitCount

Sorry, yes a typo. This is not copied directly from my code but constructed just for this inquiry.


to OOper:

Yeah... I can't think of another way to "not use optional arrays".

I love seeing your mystical syntax code. I'll try it. I'm not fluent with closures. I've used them of course, but still find them mystical.


to all:

I have failed in my request.

I am looking for a way to "for loop" through Act's vars [a, b, c, ...,z] so I can apply any of the above methods.

I have tried in vain to use enum.

I have also considered making the vars a...z another level in the array so that the class Act has just one var, call it alpha then I could:

for mychar in alpha{ insert line 10 here }

But this would be a major rewrite of my app. I thought there might be a better way to loop through the members of the class.

I have rechecked my code, you are right about line 04 being useless, b[index] cannot be nil because it was included in the Act.b!.count.

Thanks!

I took a closer look at your line 06

" var bToz : [[UInt32]?] = [b, c, d, z] // You have to type all var names "

If I make Act.a:[UInt32], even though it would hold just one UInt32, a var aToz could be added within the class declaration:

var aToz: [[UInt32]?] { return [ a, b, c, d...., z] }

The beauty of which is that it needs no initializer, so my init's throughout the code are not affected. All I had to do was put brackets, [ ], around every existing reference to Act.a Not as painful as I thought it might be.


Thanks for the inspiration!

line 04:
- b is not nil so b? is useless, use b!
- how could b![index] be nil ?

It might be that not all b are occupied, e.g., just the first in the array. My fault for not specifying every detail: towit, e.g., b can have up to four members in the array, or none.


b is an optional array (not an array of optionals.

So if b is not nil, b![index] cannot be nil ; it can crash for out of bounds index, but not be nil.


I tested the exact following code in playground, to loop through all vars (from b to z)

I renamed vars as bB, cC… because I already used b, c…:


var totalBits = 0
var bB: [UInt32]?
var cC: [UInt32]? = [125]
var dD: [UInt32]?
var zZ: [UInt32]? = [4123]
var bToz : [[UInt32]?] = [bB, cC, dD, zZ]     // You have to type all var names

for xx in bToz {
    if xx != nil {
        for index in 0 ..< xx!.count {
            totalBits += xx![index].nonzeroBitCount
        }
    }
}
print(totalBits)
totalBits = 0
for xx in bToz {
     if xx != nil {
         for item in  xx! {
             totalBits += item.nonzeroBitCount
         }
     }
}
print(totalBits)
totalBits = 0
for xx in bToz where xx != nil {
    totalBits += xx!.reduce(0) { $0 + $1.nonzeroBitCount }
}
print(totalBits)



I get the following correct results:

11

11

11

Just to be clear, I can now code a for-loop to go through just-the-vars-I-want:


  1. class Act: Codable {
  2. var a: [Uint32]
  3. var b: [UInt32]?
  4. ...
  5. var z: [UInt32]?
  6. var aToz: [[UInt32]?] { return [ a, b, c, d...., z] }
  7. init( ...vars...)
  8. }

...

100. for member in Act.aToz { if member != nil {for index in 0..<member!.count { totalBits += member?[index].nonzeroBitCount } } }


A further benefit is that when my "class Act" is written to a JSON file, the computed var "aToz" is not in it, not even a mention of it. So the file size is not "doubled" by adding the computed var! Love it.


I want to rework : "let totalBits = act.b?.reduce(0, {$0 + $1.nonzeroBitCount}) ?? 0" and use it eventually... it's beautiful!

If I make Act.a:[UInt32],

That was my very first question: quote 'All var are arrays, except a. Is it on purpose ?'



So, is it solved ?


If so, don't forget to close the thread by marking the 'inspiration' answer.


Good continuation.

"b is an optional array (not an array of optionals."

UhOh. You're right again. I have to rethink this. The array Act.a must exist and be non-nil, but all other vars Act.b thru z need not have values. Nor arrays then? Might I need optional arrays of optionals? Now I feel like I stepped in something. :-(

You need to be very clear on what you get in var a to z.

Where are those UInt coming from ?

Optional are useful if you need to have a value:

- for instance, if each var was to contain exactly 6 items, you could declare the arrays as [UInt32?]

- but in that case, the array itself doesn't need to be optional


In your case, I understand you will have a variable number of items in arrays:

- a will have one,

- b may have 6

- c may have 0.

But all var a, b, c exist.

So, no need to be optional, but just declare initially as empty (hence no init needed):

var a : [UInt32] = []. // or var a = [UInt32]()


With this, your code becomes (tested in playground):

var totalBits = 0
var a: [UInt32] = []
var b: [UInt32] = []
var c: [UInt32] = []
var d: [UInt32] = []
var z: [UInt32] = []
var aToz : [[UInt32]] = []     // You have to type all var names
 
func computeTotal() -> Int {
     var total = 0
     for xx in aToz {
         total += xx.reduce(0) { $0 + $1.nonzeroBitCount }
     }
    return total
}

// Somewhere, you will populate the arrays (getting a JSON Request, typing by user, … whatever way ; here I set manually)
func populateVar() {
      a = [100]
      b = [50, 200]
      c = [125, 6, 314]
      d = []
      z = [4123]
      aToz = [a, b, c, d, z]     // You have to type all var names
 
     totalBits = computeTotal()
     print(totalBits)
}
 
populateVar()

I get the result in log:

27


As a conclusion

It is better here to have the same type for all, even to hold a single value or no value.

Globally, you should avoid adding optional everywhere

I tried using optional arrays of optionals. It just made my code messy. It was easier to deal with a formed array that had a zero value than add a couple hundred "!" to optionals calls.


btw: the code "var aToz : [[UInt32]] = []" may work outside a class declaration, as in a playground, but fails within one.


" var aToz: [[UInt32]?] { return [ a, b, c, d...., z] } " works inside a class. You lead me to that and I am thankful. My code works now.

New here. What's the "inspiration" answer?

I hope you got a point for my clicking "This helped me" but I don't know if you did.

I don't understand your points:


I tried using optional arrays of optionals. It just made my code messy. It was easier to deal with a formed array that had a zero value than add a couple hundred "!" to optionals calls.


I just proposed to REMOVE optionals


btw: the code "var aToz : [[UInt32]] = []" may work outside a class declaration, as in a playground, but fails within one.


I just tested 1 minute ago, in a class or inside a func, it works perfectly OK of course.

But you need to initialize its content somewhere.

What is the error you get to understand the error you made ?


Anyway, it is good to know it works, don't forget to close the thread.

I stand corrected. I just copied and pasted "var aToz : [[UInt32]] = []" into my class and it was accepted this time. Don't know what i did the first time.

As for the optionals: my actual code creates UInt32=zero for a finite number of members of any array that has at least one real value. So if a given array has just one real value the other members get zero, and it is easier to if=zero than to optionalize (to nil)... you know what I mean, and then if!=nil. Maybe that's as clear as mud but without showing the actual code it would take too much explaining.


As for "initializing the content" of the aToz, thats what "var aToz: [[UInt32]?] { return [a, b, c, ... z] } " does. That is to say, the closure does the initialization, as I understand it.


My happy results are found @: G.U.T.explorerMar 24, 2020 9:42 AM