How to implement 'var selectedRowIndexes: IndexSet { get }'

I do not understand how this may be used in a code. In particular I do not understand { get }.

Declarations in the framework's manuals like these are useless if no more info is given.

And there are a lot of these shortcuts. Manuals should not play peekaboo


Thanks for any hint where to find some more explicit info.

gefa

Answered by Claude31 in 411112022

The fix is pretty simple, just as you do with any other optional:


        var r_RowSelection : [IndexPath] = []
        var r_RowSelectionDictionary: [String : IndexSet] = [:] // Or r_RowSelectionDictionary = Dictionary < String, IndexSet >()

       if codeSystemTableView.indexPathsForSelectedRows == nil {
          // May be you have to set r_RowSelectionDictionary = [:]
          return // from the func you are in ; maybe you have to return something
       }

// Now on, selection is not nil, and the following is safe

        r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
        let selectedRows = r_RowSelection.map() { $0.row}
        let selectedRowsSet = IndexSet(selectedRows)

        r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]


For your other points:

In addition I have some questions:

• I couldn't find in the manuals the function map(). I want to know something more about this function and the statement

'r_RowSelection.map() { $0.row}'

• I don't understand { $0.row} in particular not the part ' $0'

It would be very helpful if you can give me a link where I can read more about these secrets.


map() is a very powerful func on collections (as arrays): it goes through all the elements, transform them as described in the closure,

Here is what it r_RowSelection.map() { $0.row} is doing:

- will create a new array: selectedRows, derived from r_RowSelection

- $0 is the way to reference the current item in the array

- for each item ($0) in r_RowSelection, get its row

- get this item in the new array, selectedRows


There is a less condensed form that is easier to understand:

r_RowSelection.map({ (item: IndexPath) -> Int in

let result = item.row

return result

})


Should have a look at « The Swift Programming Language (Swift 4). » Apple Books. to learn more

Also read this well written tutorial

h ttps://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

You'll learn about other very important func, as filter and reduce.


If everything works OK after those explanation, don't forget to close the thread. Otherwise, don't hesitate to ask.

That means that this var is get only: you can get its value, you cannot set it (at least directly).

Ok, that's really sucint information, but this concept of get or set properties is very basic to Swift, you'll rapidly get trained to it.


To set the value, need to use

func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)

Hello Claude31,


Please apologize: I want to come back to a problem that seemed to be solved. I realized it isn't solved. I want to retrieve from a UITableView which rows are selected. Here is an excerpt of my code:


var r_RowSelection: NSMutableIndexSet = NSMutableIndexSet.init(index: 0)


@IBOutlet var codeSystemTableView: UITableView!

@IBAction func switchTasks(_ sender: Any)

{

r_RowSelection = codeSystemTableView.indexSet

}


I get the error message:


Value of type 'UITableView' has no member 'indexSet'


What is wrong?



Thank you for a hint what needed to be done.

gefa

Need to change as:


  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows



Your declaration for the var does not match either.


Should be:

var r_RowSelection : [IndexPath] = []


You don't tell what you want to do with r_RowSelection

Probably you will have to adapt your current code. Show it if you need help.

Hello Claude31,


thank you very much for your advice. My problem is solved. Nonetheless, you anticipated a new problem when remarking:


"You don't tell what you want to do with r_RowSelection

Probably you will have to adapt your current code. Show it if you need help."


That's true. What I want to do is to save the row selection in a dictionary. Anticipated too - I wasn't successful. Here my code:


var r_RowSelectionDictionary: Dictionary <String, NSMutableIndexSet> = [:]

r_RowSelection = codeSystemTableView.indexPathsForSelectedRows

This works fine. But the next line produces an error message:

r_RowSelectionDictionary = [r_TemplateTerm : r_RowSelection]


The error message:

"Cannot convert value of type '[IndexPath]?' to expected dictionary value type 'NSMutableIndexSet' "


Please help me to solve this problem. Again I complain, manuals with 'abbreviated' declarations look good but are no help at all.


Thank you very much.

gefa

  var r_RowSelectionDictionary: Dictionary <string, nsmutableindexset=""> = [:]
…
  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows
               This works fine. But the next line produces an error message:
  r_RowSelectionDictionary = [r_TemplateTerm : r_RowSelection]



The error message:

"Cannot convert value of type '[IndexPath]?' to expected dictionary value type 'NSMutableIndexSet' "


tells you:

r_RowSelection is an optional array of IndexPath

NSMutableIndexSet is essentially a set of Int.


So, the following compiles:

        var r_RowSelection : [IndexPath] = []
           var r_RowSelectionDictionary: Dictionary <string, nsmutableindexset=""> = [:]
        r_RowSelection = tableView.indexPathsForSelectedRows!
        let selectedRows = r_RowSelection.map() { $0.row}
        let selectedRowsSet = IndexSet(selectedRows) as! NSMutableIndexSet
                       
           r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]



But you could better change the type oto IndexSet

        var r_RowSelection : [IndexPath] = []
        var r_RowSelectionDictionary: Dictionary <string, indexset=""> = [:]

        r_RowSelection = tableView.indexPathsForSelectedRows!
        let selectedRows = r_RowSelection.map() { $0.row}
        let selectedRowsSet = IndexSet(selectedRows)
                       
           r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]



Hope that will work.

If so, thanks to confirm by closing the thread.

Hello Claud31,


I tried your second proposal


var r_RowSelection : [IndexPath] = []

var r_RowSelectionDictionary: Dictionary <String, IndexSet=""> = [:]


and run into the following error:

Expected '>' to complete generic argument list


As can be seen I capitalized 'indexset' into 'IndexSet'. I hope that's correct.


What's wrong?


gefa

Hello Claud31,


I tried your second proposal


var r_RowSelection : [IndexPath] = []

var r_RowSelectionDictionary: Dictionary <String, IndexSet=""> = [:]


and run into the following error:

Expected '>' to complete generic argument list


As can be seen I capitalized 'indexset' into 'IndexSet'. I hope that's correct.


What's wrong?


gefa

The editor here is not helping.


I meant:



var r_RowSelection : [IndexPath] = []

var r_RowSelectionDictionary: [String : IndexSet] = [:] // Or r_RowSelectionDictionary = Dictionary < String, IndexSet >()


r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!

let selectedRows = r_RowSelection.map() { $0.row}

let selectedRowsSet = IndexSet(selectedRows)

r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]

Hello Claud31,


thank you for your answer. The code can be compiled like in these lines you proposed:


r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!


let selectedRows = r_RowSelection.map() { $0.row}

let selectedRowsSet = IndexSet(selectedRows)

r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]


OK. But now a new problem comes up when running the code. From the user's point of view it is valid that no row at all is selected, i.e. r_RowSelection can be nil. Additionally, nil need to be the default state of r_RowSelection. This produces the following error message:


Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value


How to solve this problem? I tried:

var r_RowSelection : [IndexPath]? = []

r_RowSelection = codeSystemTableView.indexPathsForSelectedRows

instead of

var r_RowSelection : [IndexPath] = []

r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!



But this produces another error message

Value of type '[IndexPath]' has no member 'row'

in the code line:

let selectedRows = r_RowSelection.map() { $0.row}



I am totally helpless.


In addition I have some questions:

• I couldn't find in the manuals the function map(). I want to know something more about this function and the statement

'r_RowSelection.map() { $0.row}'

• I don't understand { $0.row} in particular not the part ' $0'

It would be very helpful if you can give me a link where I can read more about these secrets.


Best Regards

gefa

Accepted Answer

The fix is pretty simple, just as you do with any other optional:


        var r_RowSelection : [IndexPath] = []
        var r_RowSelectionDictionary: [String : IndexSet] = [:] // Or r_RowSelectionDictionary = Dictionary < String, IndexSet >()

       if codeSystemTableView.indexPathsForSelectedRows == nil {
          // May be you have to set r_RowSelectionDictionary = [:]
          return // from the func you are in ; maybe you have to return something
       }

// Now on, selection is not nil, and the following is safe

        r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
        let selectedRows = r_RowSelection.map() { $0.row}
        let selectedRowsSet = IndexSet(selectedRows)

        r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]


For your other points:

In addition I have some questions:

• I couldn't find in the manuals the function map(). I want to know something more about this function and the statement

'r_RowSelection.map() { $0.row}'

• I don't understand { $0.row} in particular not the part ' $0'

It would be very helpful if you can give me a link where I can read more about these secrets.


map() is a very powerful func on collections (as arrays): it goes through all the elements, transform them as described in the closure,

Here is what it r_RowSelection.map() { $0.row} is doing:

- will create a new array: selectedRows, derived from r_RowSelection

- $0 is the way to reference the current item in the array

- for each item ($0) in r_RowSelection, get its row

- get this item in the new array, selectedRows


There is a less condensed form that is easier to understand:

r_RowSelection.map({ (item: IndexPath) -> Int in

let result = item.row

return result

})


Should have a look at « The Swift Programming Language (Swift 4). » Apple Books. to learn more

Also read this well written tutorial

h ttps://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

You'll learn about other very important func, as filter and reduce.


If everything works OK after those explanation, don't forget to close the thread. Otherwise, don't hesitate to ask.

Hello Claude31,


great … formidable … my problem is solved … thanks to your … grâce â votre … excellent advices.

In particular, the addional comments were very helpful


Thank you very much. I will check 'Correct Answer'


gefa

Thanks for feedback.


To be perfect, you should mark as correct the real answer itself (my post Mar 22, 2020 5:56 AM), not your own feedback.


Take care of you and have a good day.


One last point. You should take care to select very explicit names for var, func, whatever. That will really help you to code and even more to maintain code.

For instance, you could change

var r_RowSelection : [IndexPath] = []

with

var indexesSelection : [IndexPath] = []

To make it clear you deal with indexPath, not rows, that there are several (array) ; in addition have a more swifty name (no underscore)


in the same way, change

var r_RowSelectionDictionary: [String : IndexSet] = [:]

with

var indexesSelectionDict: [String : IndexSet] = [:]



That will make the following much easier to understand. Instead of

        r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!     // Is it a selected row ? No
        let selectedRows = r_RowSelection.map() { $0.row}
        let selectedRowsSet = IndexSet(selectedRows)
        r_RowSelectionDictionary = [r_TemplateTerm : selectedRowsSet]               // Is it a dict of rows ? No

You will have:

        indexesSelection = codeSystemTableView.indexPathsForSelectedRows!     // This is a selection of indexes
        let selectedRows = indexesSelection.map() { $0.row}                                  
        let selectedRowsSet = IndexSet(selectedRows)
        indexesSelectionDict = [r_TemplateTerm : selectedRowsSet]               // This is a dict of indexes

Hello Claude31,


the problem we discussed is solved. Ok. Unfortunately, a new row selection problem has arised in a broader context. To understand, I need to describe this context a bit more than already done. There are the adapted lines of code you proposed:


r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!

let selectedRows = r_RowSelection.map() { $0.row}

r_RowSelectionDictionary[r_TemplateTerm] = selectedRows


r_SelectedRowIndexes = IndexSet(selectedRows)


Everything is ok.


The broader context: The codeSystemTableView should be able to display more that one table - not simultaneously within one and the same view, but successively. This is done by operating a button that iterates through the available tables. This button calls a function that runs the following code:


DispatchQueue.main.async

{

self.codeSystemTableView.reloadData()

}


That’s ok too. The different tables can be displayed and its rows can be selected or unselected. Understandably, the selection on a particular table vanishes between the iterations, i.e. after reloadData(). Therefore user cannot check the selection state of the different tables by iterating through the tables. To tackle this problem the selections on the different tables is layed down in a dictionary as shown in the code lines above. To restore the selections the dictionary’s selectedRows are cast into an IndexSet as you proposed:


r_SelectedRowIndexes = IndexSet(selectedRows)


An IndexSet is needed to set the table’s row selection state by means of this function according to Xcode’s manual:

func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)

Sets the row selection using indexes extending the selection if specified.


Now, the new lines of code look like these:

r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!

let selectedRows = r_RowSelection.map() { $0.row}

r_RowSelectionDictionary[r_TemplateTerm] = selectedRows


r_SelectedRowIndexes = IndexSet(selectedRows)

codeSystemTableView.selectRowIndexes(r_SelectedRowIndexes, YES)


1. Problem. The last line throws an error:

Value of type 'UITableView' has no member 'selectRowIndexes' contradictory to what Xcode’s documents say.


2. Problem. Printing r_RowSelection, selectRowIndexes, r_RowSelectionDictionary shows:

r_RowSelection is [[0, 0], [0, 2], [0, 4], [0, 6]])

r_SelectedRowIndexes is 4 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]]


The r_RowSelectionDictionary contains one item. That’s ok because until now only one table has been activated. Iterating to the next table shows the following printout.

r_RowSelection is [[0, 0]])

r_SelectedRowIndexes is 1 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6], "Template 2": [0]]


That’s ok too. Now, two tables have been ativated and some items selected. But what is wrong or irritating, the r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4 indexes resp. 1 indexes:

r_SelectedRowIndexes is 4 indexes

r_SelectedRowIndexes is 1 indexes


Some additional information to understand the code:

The tables are represented by the keys "Template 1" and "Template 2". These keys refer to the appropriate tables. That’s ok too. For testing and demonstration there are two tables only. The number of tables is unrestricted, at least theoretically.


Do you have an idea how to solve these problems?

Again, thank you very much for your help.


Best regards

gefa

May I remind first some good practices on the forum :

- Avoid reusing an existing closed thread to ask a new question. That makes endless posts, very hard to read. And confuses reader who thinks the issue is over.

=> Create a new thread, copy your new post there, possibly giving a link to the first thread if needed


- When you mark a correct answer, mark the correct answer itself, not your own answer.

=> Unmark your post and mark correct the real answer (post dated Mar 22, 2020 5:56 AM)


- When you post code, please use the formatter tool (<>):

=> Select each piece of code and hit <>


Instead of

r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!

let selectedRows = r_RowSelection.map() { $0.row}

r_RowSelectionDictionary[r_TemplateTerm] = selectedRows


that gives

  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows



I will analyze your mail and answer in the new thread.

Waiting you do the 3 points…


Edited:

I have answers to your questions.

Waiting you complete the 3 points.

Hello Claude31,


I want to apologize for the misuse. I didn't know that and I will confirm to these rules in the future


gefa

How to implement 'var selectedRowIndexes: IndexSet { get }'
 
 
Q