I would like to be able to sort a multiple column list view
progressively column by column depending on which headers are pressed. Eg 3 columns: name age sex If I press heading "name" then "sex" then "age" the order should be, name by sex by age. The sort order block within each column allows the ability to sort by that column or a multiple of columns but only in a predefined way. I would like to have a dynamic way of determining which column to sort by. To do this I need to get feedback each time a heading is pressed. Ie the question is: how do I know "age" has been pressed? Any help would be appreciated. Thanks Theo |
Theo wrote:
> > I would like to be able to sort a multiple column list view > progressively column by column depending on which headers are pressed. > > Eg 3 columns: name age sex > If I press heading "name" then "sex" then "age" the order should be, > name by sex by age. Just to be clear. I take this to mean that the primary sort is the last column selected? So in your example, it would be first sorted by age, and then within the same age sorted by sex, and finally within the same age and sex sorted by name? I just wanted to point out that this would occur naturally if sorting would just preserve the existing order for equal entries. Unfortunately this is not the case for the quicksort algorithm used by SortedCollection. There have been a number of times that I wished it were otherwise. :-( But, if I misunderstood your order of column selections, then there would also need to be some user action to indicate that they were starting over and selecting the primary sort column. Anyway, as far as doing what you want to do ... Check out ListView>>lvnColumnClick: which calls ListView>>sortOnColumn: which calls ListPresenter>>beSorted: for places to override or hook in. You'll need to keep track of what columns have been clicked, and then use an appropriate composite sort block. ------------------------------------------- Bill Dargel [hidden email] Shoshana Technologies 100 West Joy Road, Ann Arbor, MI 48105 USA |
"Bill Dargel" <[hidden email]> wrote in message
news:[hidden email]... > Theo wrote: > > > > I would like to be able to sort a multiple column list view > > progressively column by column depending on which headers are pressed. > > > > Eg 3 columns: name age sex > > If I press heading "name" then "sex" then "age" the order should be, > > name by sex by age. > > Just to be clear. I take this to mean that the primary sort is the last > column selected? So in your example, it would be first sorted by age, > and then within the same age sorted by sex, and finally within the same > age and sex sorted by name? > > I just wanted to point out that this would occur naturally if sorting > would just preserve the existing order for equal entries. Unfortunately > this is not the case for the quicksort algorithm used by > SortedCollection. There have been a number of times that I wished it > were otherwise. :-( I think Bob Jarvis has granted your wish (see StableSortedCollection, he also includes a list presenter for it): http://www.nls.net/mp/jarvis/Bob/DolphinGoodies.htm I have used the StableSortedCollection (Thanks Bob!), but not the Presenter yet. Chris |
Christopher J. Demers wrote:
> I think Bob Jarvis has granted your wish (see StableSortedCollection, he > also includes a list presenter for it): > http://www.nls.net/mp/jarvis/Bob/DolphinGoodies.htm > > I have used the StableSortedCollection (Thanks Bob!), but not the Presenter > yet. Thanks for the pointer Chris! I've been checking it out, and have some fixes and further caveats for you, Theo, or anyone else. I've stuck the added and changed code at the end of this message. I noticed a potential bounds problem with the indices into tempArray, while inspecting the code. Finally came up with the setup shown in #testTempArrayLargeEnough, that causes the error to surface. The key is having 'firstIndex' no longer be 1, and a large enough #addAll: that the space at the beginning of the collection gets left. The fix then is in the attached #sortFrom:to: where tempArray simply needs to be instantiated from #basicSize, rather than #size. I tried out using the StableListPresenter by hacking MethodBrowser>>createComponents. Only found that the list no longer sorted at all. The fix to StableListPresenter>>list: is attached. It's that age-old gotcha of the value answered from #addAll: being the added collection, and not the modified receiver collection. Just needed a #yourself. BTW, the two tests #testStability1 and #testStability2 suffered from the same #addAll: usage problem and were not really testing what they appeared to be testing. (One could substitute SortedCollection for StableSortedCollection, and they still would pass). The fixed tests below fail on SortedCollection, but pass for StableSortedCollection. Anyway, with the fix to StableListPresenter>>list: the hacked MethodBrowser started doing useful things. If you clicked on the 'Class' column, and then on the 'Selector' column for instance, the entries with the same selector would have a sub-sort on class. But, after playing with it for a while, I realized that the 'preserved order' was actually being reversed. In other words, if you sorted on class A-Z, and then sorted by selectors, the classes within equal selectors would be Z-A. Some debugging and playing with variations of the stability tests (once they'd been fixed) revealed what the issue is. The correct operation of StableSortedCollection depends on the sortBlock answering true for equal values. But the MethodBrowser has a sort that is "a selector < b selector". The historical Smalltalk sortBlock was (and default in SortedCollection still is in Dolphin) "a <= b". Though now the ANSI standard says it should be "a < b". The StableSortedCollection will need to be modified to correctly handle "<" style sortBlocks. To transparently handle both "<" or "<=" styles, it would need to do a check on "sortBlock value: someElement value: someElement" and see if it was true or false for the equal case, and then act accordingly. I've already spent too much time today playing with this, rather than getting done the work that I should be doing. So saying, I probably won't do any more on it without a specific need arising. I wonder if Andy and Blair could be enticed into picking up on it? ;-) It would be nice if in particular the MethodBrowser could do secondary sorts on the other columns. regards, -Bill ------------------------ !StableSortedCollectionTest methodsFor! testTempArrayLargeEnough | collection errors last | collection := StableSortedCollection new. collection addAll: (100 to: 200). 60 timesRepeat: [collection removeFirst]. collection addAll: (130 to: 200). last := nil. errors := 0. collection do: [:each | (last notNil and: [last > each]) ifTrue: [errors := errors + 1]. last := each]. self assert: errors = 0! ! !StableSortedCollectionTest categoriesFor: #testTempArrayLargeEnough!public!unit tests! ! sortFrom: start to: stop "Private - Sort elements start through stop of self to be nondescending according to sortBlock." "wod - Fix bug where tempArray needs to be initialized to basicSize. Otherwise in may not be large enough when the internal 'firstIndex' is something other than 1." tempArray := Array new: self basicSize. self mergeSortFrom: start to: stop. tempArray := nil! ! !StableSortedCollection categoriesFor: #sortFrom:to:!private!sorting! ! !StableListPresenter methodsFor! list: aSequenceableCollection "Set the contents of the receiver to be aSequenceableCollection" "wod - fix bug so that the list is the StableSortedCollection and not the original aSequenceableCollection." | list | list := self isSorted ifTrue: [(StableSortedCollection new sortBlock: sortBlock) addAll: aSequenceableCollection; yourself] ifFalse: [aSequenceableCollection]. ^self model list: list! ! !StableListPresenter categoriesFor: #list:!accessing!public! ! !StableSortedCollectionTest methodsFor! testStability1 "Verify that the sort is stable" "wod - fix bug so that it is actually verifying the StableSortedCollection" | testData col lastTestData errs | testData := OrderedCollection new. testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new value: n]). testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new value: n]). testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new value: n]). 1 to: testData size do: [:i | (testData at: i) order: i]. col := StableSortedCollection new sortBlock: [:v1 :v2 | v1 value <= v2 value]. col addAll: testData. lastTestData := nil. errs := col inject: 0 into: [:tot :aSortTestData | lastTestData notNil ifTrue: [lastTestData value = aSortTestData value ifTrue: [lastTestData order > aSortTestData order ifTrue: [tot := tot + 1]]]. lastTestData := aSortTestData. tot]. self should: [errs = 0]! ! !StableSortedCollectionTest categoriesFor: #testStability1!public!unit tests! ! !StableSortedCollectionTest methodsFor! testStability2 "Verify that the sort is stable" "wod - fix bug so that it is actually verifying the StableSortedCollection" | testData col lastTestData errs | testData := OrderedCollection new. (25 to: 1 by: -1) do: [:i | 1 to: 100 do: [:j | testData add: (SortTestData new value: i)]]. 1 to: testData size do: [:i | (testData at: i) order: i]. col := StableSortedCollection new sortBlock: [:v1 :v2 | v1 value <= v2 value]. col addAll: testData. lastTestData := nil. errs := col inject: 0 into: [:tot :aSortTestData | lastTestData notNil ifTrue: [lastTestData value = aSortTestData value ifTrue: [lastTestData order > aSortTestData order ifTrue: [tot := tot + 1]]]. lastTestData := aSortTestData. tot]. self should: [errs = 0]! ! !StableSortedCollectionTest categoriesFor: #testStability2!public!unit tests! ! ------------------------------------------- Bill Dargel [hidden email] Shoshana Technologies 100 West Joy Road, Ann Arbor, MI 48105 USA |
Free forum by Nabble | Edit this page |