I have a large list of items which I want to group by condition and display them in table columns. Which is the preferred way to do this?
Can paging library help me with that?
I tried adding items to MutableList<List<Item>> (index, item) but I get slow performance.
private fun createData(idSection: Int) {
println("idSection = $idSection")
viewModelScope.launch {
showDialogLoading(true)
delay(3000)
scoreWithStudentScoreAndStudent()?.collect {
cells.clear()
val filtered = it.filter { scoreWithStudentScoreAndStudent ->
scoreWithStudentScoreAndStudent.studentScoreAndStudent.firstOrNull()?.studentScore?.idSection == idSection
}
println("${filtered.size}")
if (filtered.isNotEmpty()) {
println("NOT EMPTY")
_scores.value = filtered.map { scoreWithStudentScoreAndStudent ->
scoreWithStudentScoreAndStudent.score
}
filtered.forEachIndexed { index, scoreWithStudentScoreAndStudent ->
_test.value =
scoreWithStudentScoreAndStudent.studentScoreAndStudent.sortedWith(
compareBy<StudentScoreAndStudent> { studentScoreAndStudent ->
studentScoreAndStudent.student.lastName
}
.thenBy { studentScoreAndStudent ->
studentScoreAndStudent.student.firstName
}
)
_students.value = _test.value.map { studentScoreAndStudent ->
studentScoreAndStudent.student
}
cells.add(index, _test.value)
}
showDialogLoading(false)
//showDialogLoading(false)
scoresTableVisibility(true)
} else {
scoresTableVisibility(false)
showDialogLoading(false)
textAndIconVisibility2(false)
}
}
scoresTableVisibility(false)
}
}
Related
I have an array of questions and each question have a unique lineID.
The problem is when I sync the questions from the server sometimes (1 time at 1000 syncs ) the questions are duplicated. Instead of 28 questions I have 56.
I want somehow to filter the array of questions and if there are 2 questions with the same lineID (duplicated) then to append only 1 into my array of objects.
Can anyone help me with this issue ?
// Get the questions from CoreData.
func loadQuestionsUsing(templateId: Int) {
do {
guard let questions = try Question.getQuestions(templateId: templateId, context: context) else { return }
checklistSections = []
questions.forEach { question in
// If the question lineID is duplicated, then select only one of that 2 duplications.
// code here
print(“Question Line ID: \(question.lineId)“)
// If the question have the same templateID as the Checklist Template which user selected from the menu then create an object with that question properties.
if question.templateId == templateId {
let showVehicle = question.vehicle == 1
let showTrailer = question.trailer == 1
let highlight = question.highlight == 1
let pg9 = question.pg9 == 1
let question = ChecklistQuestion( rowID: Int(question.id),
templateID: Int(question.templateId),
lineID: Int(question.lineId),
poolID: Int(question.poolId),
descript: question.descript ?? String(),
showVehicle: showVehicle,
showTrailer: showTrailer,
header: question.header ?? String(),
highlight: highlight,
pg9: pg9,
imagesPath: [])
let header = (question.header.isEmpty) ? Constants.questionsNoHeader : question.header
// check if question.header (Section) exist in the ChecklistItemSection
if let existingSection = checklistSections.firstIndex(where: { $0.name == header }) {
// If section exist then append the ChecklistItem (question) to that existing section.
checklistSections[existingSection].questions.append(question)
} else {
// If section don’t exist then create the section (question.header) and also append the question to that section.
checklistSections.append(ChecklistSection(name: header, questions: [question]))
}
}
}
} catch {
print(“Error while fetching the Questions from CoreData: \(error.localizedDescription)“)
}
print(“CHECKLIST CONTAINS: \(checklistSections.count) Sections.“)
}
Struct for ChecklistSection:
class ChecklistSection {
var name: String // Name of the section
var questions: [ChecklistQuestion] // List with all questions from a Section
init(name: String, questions: [ChecklistQuestion]) {
self.name = name
self.questions = questions
}
}
My stupid solution:
func findDuplicatedQuestions(checklistSections: [ChecklistSection]) -> [ChecklistSection] {
var duplicates: [ChecklistQuestion] = []
var prevQuestionLineID: Int = 0
var addedQuestionLineID: Int = 0
checklistSections.forEach { section in
section.questions.forEach { question in
if (prevQuestionLineID == question.lineId && addedQuestionLineID != question.lineId) {
duplicates.append(question)
addedQuestionLineID = question.lineId
}
prevQuestionLineID = question.lineId
}
}
return checklistSections
}
Thanks for reading this !
Conform your ChecklistQuestion to Equatable, implement == so it knows how to compare two instances.
class ChecklistQuestion: Equatable {
static func ==(lhs: ChecklistQuestion, rhs: ChecklistQuestion) -> Bool {
return lhs.lineID == rhs.lineID
}
}
Then using this extension to remove duplicate items:
extension Array where Element: Equatable {
/// Remove Duplicates
var unique: [Element] {
// Thanks to https://github.com/sairamkotha for improving the method
return self.reduce([]){ $0.contains($1) ? $0 : $0 + [$1] }
}
}
Usage:
var questions: [ChecklistQuestion] = ...
questions.unique
//or
var uniqueQuestions = questions.unique
Extension credits to https://github.com/dillidon/alerts-and-pickers
In case you need preserve order and still use arrays (and run in O(n)). Use this:
func getUnique(questions: [ChecklistQuestion]) -> [ChecklistQuestion] {
var set = Set<ChecklistQuestion>()
var res = [ChecklistQuestion]()
for question in questions {
if !set.contains(question) {
res.append(question)
set.insert(question)
}
}
return res
}
And you need to implement Hashable protocol for your model
I am creating a wizard using UICollectionView with an array of CollectionViewCells:
var viewCells:[BaseCVCell] = [createEventSubjectSearch(), createEventEventForm()]
This array is dynamically added to based on a series of UISwitch's that the user controls. I can add to the array fine using the code below, however I can't seem to remove an item when a user turns the switch off.
func switchToggled(sender : UISwitch) {
if sender == createDiarySwitch {
if sender.isOn {
parentClass?.viewCells.append(createEventDeferEvent())
} else {
if let i = parentClass?.viewCells.index(where: { $0 == createEventDeferEvent() }) {
parentClass?.viewCells.remove(at: i)
}
}
}
if sender == createDeferredSwitch {
if sender.isOn {
parentClass?.viewCells.append(createEventDiariseEvent())
} else {
if let i = parentClass?.viewCells.index(where: { $0 == createEventDiariseEvent() }) {
parentClass?.viewCells.remove(at: i)
}
}
}
parentClass?.wizardCollectionView.reloadData()
}
I have tried the above code, as well as:
if let index = parentClass?.viewCells.index(of: createEventDiariseEvent()) {
parentClass?.viewCells.remove(at: index)
}
Neither approach works (no errors, the code just never returns a value). I'd like to try and avoid naming elements where possible. Is there a way to do this?
Thanks for your answers, DonMag
I've achieved the desired functionality by instanciating the two dynamic cells in the main class:
let diariseCell : createEventDiariseEvent()
and then in the loop calling as thus:
if sender == createDiarySwitch {
if sender.isOn {
parentClass?.viewCells.append((parentClass?.diariseCell)!)
} else {
if let i = parentClass?.viewCells.index(where: { $0 == parentClass?.diariseCell }) {
print("Found cell reference at index \(i)")
parentClass?.viewCells.remove(at: i)
}
}
}
Works a charm now. Amazing what another pair of eyes can pick out!
I want to be able to chain functions as seen below and choose between them. In this contrived example, I hardcoded "function_b", but in the ideal world, I'd like it to be random or even controlled by a server. I know that I can create the same effect in different ways, but I like the way this code reads, so I want to do it like this.
ExecutionManager("function_b").add("function_a") {
//some code
}.add("function_b") {
//more code
}.add("function_c") {
//other code
}
Your snipped of code does not make much sense to me. E.g. what should mean this part?
ExecutionManager("function_b")
You are just passing a string to the initializer of ExecutionManager. What should be the meaning of that?
Let's try
However if you want to be able to add a list of functions with this type
() -> ()
and then execute them (all of them or just some of them) you could define your ExecutionManager like this
class ExecutionManager {
typealias FunctionType = () -> ()
private var functions = [(String, FunctionType)]()
func add(funcName: String, function: FunctionType) -> ExecutionManager {
functions.append(funcName, function)
return self
}
func runAll() {
functions.forEach { $0.1() }
}
}
Now you can
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye") {
print("goodbye")
}.runAll()
The result is
hello
2
goodbye
Random
To run a only a function based on some logic look at this code. here I am generating a random index and the executing only one function
Extension
extension ExecutionManager {
func runRand() {
guard functions.isEmpty == false else { return }
let rand = Int(arc4random_uniform(UInt32(functions.count)))
functions[rand].1()
}
}
Example
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye") {
print("goodbye")
}.runRand()
Output
2
Update
With this version we remove the invocation of runRand at the end
#HyperZ Thanks for the hint.
import Foundation
class ExecutionManager {
typealias FunctionType = () -> ()
private var functions = [(String, FunctionType)]()
func add(funcName: String, last:Bool=false, function: FunctionType) -> ExecutionManager {
functions.append(funcName, function)
if last {
runRand()
}
return self
}
func runRand() {
guard functions.isEmpty == false else { return }
let rand = Int(arc4random_uniform(UInt32(functions.count)))
functions[rand].1()
}
}
Code
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye", last:true) {
print("goodbye")
}
Output
hello
I have create the following struct to display a tableview:
struct Section {
var heading : String
var items : [MusicModel]
init(category: String, objects : [MusicModel]) {
heading = category
items = objects
}
}
Then I have the following function to add objects to the array:
var sectionsArray = [Section]()
func getSectionsFromData() -> [Section] {
let basicStrokes = Section(category: "Basic Strokes",
objects: [MusicModel(title: Title.EightOnAHand, author: .None, snare: true, tenor: true)])
sectionsArray.append(basicStrokes)
return sectionsArray
}
So the tableview loads just fine with section header and rows for the data. I want to be able to filter the table cells but am having a tough time figuring out the proper syntax to filter an array of objects based on a user selected instrument that is sent to a filtering function. Here is what I have:
func getMusic(instrument: MusicData.Instrument) -> [Section] {
if instrument == .Tenor {
let filtered = sections.filter {$0.items.filter {$0.tenor == true}}
return filtered
} else {
let filtered = sections.filter {$0.items.filter {$0.snare == true}}
return filtered
}
}
The object is returned only if snare or tenor is true. The error that I am getting is:
Cannot invoke 'filter' with an argument list of type '(#noescape (MusicModel) throws -> Bool)'
Any idea? If you have a suggestion on better way to do this I would greatly appreciate it. Thanks!
Use this updated getMusic method,
func getMusic(instrument: MusicData.Instrument) -> [Section] {
if instrument == .Tenor {
var filtered = sections.filter {$0.items.filter {$0.tenor == true}.count > 0}
filtered = filtered.map({ (var section: Section) -> Section in
section.items = section.items.filter {$0.tenor == true}
return section
})
return filtered
} else {
let filtered = sections.filter {$0.items.filter {$0.snare == true}.count > 0}
filtered = filtered.map({ (var section: Section) -> Section in
section.items = section.items.filter {$0.snare == true}
return section
})
return filtered
}
}
Or the following one. (Be warned that your Section is struct, which means it'll be copied around.)
func getMusic(instrument: MusicData.Instrument) -> [Section] {
return sections.map { (var s: Section) -> Section in
s.items = s.items.filter { instrument == .Tenor ? $0.tenor : $0.snare }
return s
}
}
I use playground to play a little with sequences and generators.
So I created function that returns GeneratorOf struct that suppose to implement GeneratorType and SequenceType. I expect that generate will give me new full generator that I can traverse through again. But it didn't for my countDownGen2.
I suppose that I misunderstood something.
What is wrong here? and how to use GeneratorOf properly?
func countDown(start: Int) -> GeneratorOf<Int>
{
var i = start
return GeneratorOf {return i < 0 ? nil : i-- }
}
var countDownGen = countDown(10)
for i in countDownGen
{
println(i)
}
var countDownGen2 = countDownGen.generate()
for i in countDownGen2
{
println(i)
}
GeneratorOf<T>.generate() returns a copy of itself, and in your code, every copy of it shares the same reference to one i
So, when you do countDownGen2 = countDownGen.generate() after countDownGen is exhausted, countDownGen2 is also already exhausted.
What you should do is something like:
func countDown(start: Int) -> SequenceOf<Int> {
return SequenceOf { () -> GeneratorOf<Int> in
var i = start
return GeneratorOf { return i < 0 ? nil : i-- }
}
}
let countDownSeq = countDown(10)
for i in countDownSeq {
println(i)
}
for i in countDownSeq {
println(i)
}
let countDownSeq2 = countDownSeq
for i in countDownSeq2 {
println(i)
}