From: RichardOnRails Date: 2009-03-20T10:22:17+09:00 Subject: Re: Complex sort of matrix possible, e.g. like Excel? On Mar 19, 8:57 pm, RichardOnRails wrote: > On Mar 19, 5:18 pm, Rob Biedenharn > wrote: > > > > > On Mar 19, 2009, at 4:42 PM, RichardOnRails wrote: > > > > On Mar 18, 12:31 pm, Rob Biedenharn > > > wrote: > > >> On Mar 18, 2009, at 1:58 AM, Christopher Dicely wrote: > > > >>> Enumerable#sort lets you do this fairly easily with just a pure   > > >>> array > > >>> of arrays, e.g., to sort the array-of-arrays "arr" by the first   > > >>> column > > >>> ascending and the second descending: > > > >>> arr.sort {|a,b| [a[0]<=>b[0], b[1]<=>a[1]].find {|x| x!=0} || 0} > > > >> Not to detract to much from the other responses, but this ought to   > > >> be: > > > >> arr.sort {|a,b| (a[0] <=> b[0]).nonzero? || b[1] <=> a[1] } > > > >> Take a look at what Numeric#nonzero? does.  The docs specifically > > >> mention its use when chaining comparisons this way. > > > >> Doing arr.sort_by {|a| [a[0], -a[1]] } only works if the second > > >> element responds to @- (like any Numeric would, but certainly not > > >> String). > > > >> -Rob > > > >>> On 3/17/09, RichardOnRails   > > >>> > >>>> wrote: > > >>>> Hi, > > > >>>> I've got an array of rows (and thus a matrix) created user   > > >>>> FasterCSV > > >>>> to extract data from a CSV file.  I'd like to sort the matrix on > > >>>> column A ascending and, within that, column B descending.  I looked > > >>>> at > > >>>> Matrix,  but it doesn't seem to address that functionality.  Is   > > >>>> there > > >>>> a package that does, or do I have to write my own SuperMatrix > > >>>> inherited from Matrix? > > > >>>> Thanks in Advance, > > >>>> Richard > > > >> Rob Biedenharn          http://agileconsultingllc.com > > >> R...@AgileConsultingLLC.com > > > > On Mar 18, 12:31 pm, Rob Biedenharn > > > wrote: > > >> On Mar 18, 2009, at 1:58 AM, Christopher Dicely wrote: > > > >>> Enumerable#sort lets you do this fairly easily with just a pure   > > >>> array > > >>> of arrays, e.g., to sort the array-of-arrays "arr" by the first   > > >>> column > > >>> ascending and the second descending: > > > >>> arr.sort {|a,b| [a[0]<=>b[0], b[1]<=>a[1]].find {|x| x!=0} || 0} > > > >> Not to detract to much from the other responses, but this ought to   > > >> be: > > > >> arr.sort {|a,b| (a[0] <=> b[0]).nonzero? || b[1] <=> a[1] } > > > >> Take a look at what Numeric#nonzero? does.  The docs specifically > > >> mention its use when chaining comparisons this way. > > > >> Doing arr.sort_by {|a| [a[0], -a[1]] } only works if the second > > >> element responds to @- (like any Numeric would, but certainly not > > >> String). > > > >> -Rob > > > >>> On 3/17/09, RichardOnRails   > > >>> > >>>> wrote: > > >>>> Hi, > > > >>>> I've got an array of rows (and thus a matrix) created user   > > >>>> FasterCSV > > >>>> to extract data from a CSV file.  I'd like to sort the matrix on > > >>>> column A ascending and, within that, column B descending.  I looked > > >>>> at > > >>>> Matrix,  but it doesn't seem to address that functionality.  Is   > > >>>> there > > >>>> a package that does, or do I have to write my own SuperMatrix > > >>>> inherited from Matrix? > > > >>>> Thanks in Advance, > > >>>> Richard > > > >> Rob Biedenharn          http://agileconsultingllc.com > > >> R...@AgileConsultingLLC.com > > > > Hi Rob, > > > > Thanks for your response.  I don't want to be an expert on sorting > > > matrices.  I just want to get my project working. (Don't we all :-) > > > > Here's the essence of what I've got working, confirmed with debugging > > > puts'. > > > > matrix = [] > > > FasterCSV.foreach(selectedCsvFile, :headers => false) do |row| > > >    matrix << row > > > end > > > > I want (in Excel terms) the matrix sorted on column B asc. and within > > > that col. I asc.  Both columns are textual.  Based on your guidance, I > > > added the line: > > > > sortedMatrix = matrix.sort {|a,b| [a[1]<=>b[1], a[8] <=> b[8]]} > > > > Ruby gave me a syntax error: > > > ProcessExports.rb:130:in `sort': undefined method `>' for [-1, > > > 1]:Array (NoMethodError) > > > > I'm hoping the problem is that I'm invoking Array::Sort rather than > > > Enumerable::Sort but nothing my deteriorating brain could devise > > > worked.  Any ideas. > > > > Best wishes, > > > Richard > > > Well, my guidance was: > >    arr.sort {|a,b| (a[0] <=> b[0]).nonzero? || b[1] <=> a[1] } > > > Which translates to your: > >    sortedMatrix = matrix.sort {|a,b| (a[1]<=>b[1]).nonzero? || a[8]   > > <=> b[8] } > > > You might also need: > >    matrix << row.to_a > > or > >    matrix << row.fields > > in your loop, but a FasterCSV::Row probably behaves sufficiently like   > > an Array to sort properly. Whether it continues to behave later (when   > > you *really* need an Array), may resolve the question of whether you   > > need to call #to_a or #fields on your row. > > > Real code will always get you a better answer that pseudo-code. > > > If you meant for either sort on Col.B or Col.I to be *descending*,   > > then swap the a and b in the appropriate expression. (Your original   > > question had the secondary sort descending, but the latest [with   > > code ;-)] says "col. I asc.") > > > -Rob > > > Rob Biedenharn          http://agileconsultingllc.com > > R...@AgileConsultingLLC.com > > Rob, > > Thank you very much for hanging in there with me until the fog lifted. > I thought about your advice and managed to mangle it.  If I hadn't > been such a "wise guy",  I could have translated your original advice > as you did and been on my way. > > Regarding the asc/desc on the I col.,  I changed my mind midway and > decided my app requires asc. on I. > > But I have a final question:  I thought your expression had a flaw > because, in my view,  "(a[1]<=>b[1]).nonzero?", if true, should cause > the block to return true rather than plus or minus. > > So I concocted: > sortedMatrix = matrix.sort {|a,b| (v = a[1]<=>b[1]).nonzero? ? v : (a > [8] <=> b[8] )} > which works. > > But yours works, too.  So my understanding of how block evaluation > works when invoked by a calling function is flawed. > > Do you have a simple explanation of the flaw in my thinking,  or can > you point me to a relevant online tutorial on this?  But don't trouble > yourself on this; it's only icing on the cake. > > Thank you very much for the pains you took to get me going again. > > Very best wishes, > Richard OK Rob, I Googled "Ruby nonzero? (thus doing essentially what you suggest in your original response). And I see that when the expression is processed by nonzero?, the latter returns NOT true or false, but rather non-zero value or nil (which is almost 'false'). Cool! So my extra "v" is documented to be superfluous :-) Again, many thanks for your generous and excellent guidance. Best wishes, Richard