Difference between revisions of "Python/ArraySortFunction"

From Apache OpenOffice Wiki
Jump to: navigation, search
([Calc, Python] Array Sort add-in function.)
 
([Calc, Python] Array Sort add-in function.)
Line 1: Line 1:
 
{{DISPLAYTITLE:Array Sort add-in function.}}
 
{{DISPLAYTITLE:Array Sort add-in function.}}
  
This script was published by Charlie Young at the [http://forum.openoffice.org/en/forum/viewtopic.php?f=21&t=56916 Code Snippets Forum]."
+
This script was published by Charlie Young at the [http://forum.openoffice.org/en/forum/viewtopic.php?f=21&t=56916 Code Snippets Forum].
  
 
For many purposes in Calc, it is pretty easy to define data ranges with sort specifications (sort order, headers, etc.), and to redo a sort with a couple of clicks and/or keystrokes, and of course for many purposes it's better to use a database. But it might also be useful sometimes to have an array function automatically sort a range. I have examples, and others may as well. I think it's probably preferable to the tricks in Sorting and Filtering data with formulas.
 
For many purposes in Calc, it is pretty easy to define data ranges with sort specifications (sort order, headers, etc.), and to redo a sort with a couple of clicks and/or keystrokes, and of course for many purposes it's better to use a database. But it might also be useful sometimes to have an array function automatically sort a range. I have examples, and others may as well. I think it's probably preferable to the tricks in Sorting and Filtering data with formulas.

Revision as of 09:09, 28 August 2013


This script was published by Charlie Young at the Code Snippets Forum.

For many purposes in Calc, it is pretty easy to define data ranges with sort specifications (sort order, headers, etc.), and to redo a sort with a couple of clicks and/or keystrokes, and of course for many purposes it's better to use a database. But it might also be useful sometimes to have an array function automatically sort a range. I have examples, and others may as well. I think it's probably preferable to the tricks in Sorting and Filtering data with formulas.

Original code

import uno
import unohelper
from com.sun.star.lang import Locale
from com.pysorted import XPySorted
 
# PySorted Calc Add-in implementation.
 
class PySortedImpl( unohelper.Base, XPySorted ):
    def __init__( self, ctx ):
        self.ctx = ctx
 
    def PySorted(self, inRange, fieldspecs, HasHeaders, ByRows, docProps):
        aLoc = docProps.getPropertyValue("CharLocale")  #Use locale of document for collation. This could be a problem with multilingual spreadsheets.
        xCollator = self.ctx.getServiceManager().createInstance("com.sun.star.i18n.Collator")
        if not fieldspecs:
            fieldspecs = ((0,0,0,0),)
        fieldspecs = CheckFields(fieldspecs,len(inRange[0]))  #Checks fields for validity
        inRange = list(inRange)
        if len(fieldspecs) == 0:
            return tuple(inRange)  #Just echo back inRange if no valid sort fields provided.
        out = []
        if ByRows:
            inRange = zip(*inRange)
        if HasHeaders:
            out.append(inRange[0])
            inRange = inRange[1:]
 
        sortrange = main_sort(inRange,fieldspecs,xCollator,aLoc)
        out.extend(sortrange)
 
        if ByRows:
            return tuple(zip(*out))
 
        return tuple(out)
 
def main_sort(rrange,specs,xCollator,aLoc):
    for spec in specs[::-1]:
        field, rev, TextFirst, ignore_case = map(int, spec)
        xCollator.loadDefaultCollator(aLoc,ignore_case)
        rrange.sort(key=cmp2key(field,fncmp,xCollator,TextFirst),reverse = rev)
    return rrange
 
#Comparison function.  i is field index, a and b are entire rows.
def fncmp(i,a,b,xCollator,TextFirst):
    if type(a[i]) == unicode and type(b[i]) == unicode: 
        return xCollator.compareString(a[i],b[i])
    elif type(a[i]) == float and type(b[i]) == float:
        return numcmp(a[i],b[i])
    elif type(a[i]) == float:
        return 1 if TextFirst else -1
    else:
	    return -1 if TextFirst else 1
 
#Compare two numerics
def numcmp(a,b):
    if a < b:
        return -1
    elif a > b:
        return 1
    else:
        return 0
 
def cmp2key(i,mycmp,xCollator,TextFirst):
    #Convert a cmp= function into a key= function
    class K(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) < 0
        def __gt__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) > 0
        def __eq__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) == 0
        def __le__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) <= 0
        def __ge__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) >= 0
        def __ne__(self, other):
            return mycmp(i,self.obj, other.obj, xCollator, TextFirst) != 0
    return K
 
 
#Checks fieldspecs validity.
def CheckFields(inArray,maxfields):
    outArray = []
    speclen = len(inArray)
    speclen0 = len(inArray[0])
 
    fieldnumbersin = [getfieldnumber(inArray[i][0],maxfields) for i in range(speclen)]
    fieldnumbersout = []
 
    for fieldnumber in fieldnumbersin:
        if fieldnumber not in fieldnumbersout:
            fieldnumbersout.append(fieldnumber)
        else:
            fieldnumbersout.append(-1)
 
    testfields = [tuple([fieldnumbersout[i]] + [getfieldvalue(inArray[i][j]) if j < speclen0 else 0 for j in range(1,4)]) for i in range(speclen)]
 
    for i in range(len(testfields)):
        if testfields[i][0] != -1:
            outArray.append(tuple(testfields[i]))
 
    return outArray
 
def getfieldnumber(n,maxfields):
    if type(n) == float:
        return int(n) if 0 <= n < maxfields else -1
    elif type(n) == int:
        return n if 0 <= n < maxfields else -1
    else:
        return -1 
 
def getfieldvalue(n):
    if type(n) == float or type(n) == int:
        return 0 if n == 0 else 1
    else:
        return 0 
 
def createInstance( ctx ):
    return PySortedImpl( ctx )
 
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation( \
	createInstance,"com.pysorted.python.PySortedImpl",
		("com.sun.star.sheet.AddIn",),)

Process of the script

Personal tools