How can I sort alphabetically the list of objects whose names sometimes begin with numbers?

0
I have a list of objects that need to be sorted alphabetically by name. The problem is that some names start with digits, and they should be sorted as digits and not strings (1,2,3...10,11,12).   If I sort the entire list without parsing strings into digits, objects whose name starts with digits are sorted incorrectly (1,10,11,12...2,21,22). If I parse strings into numbers, there will be errors because some objects do not have numbers in the name. I could add a separate attribute so that the user manually enters the ordinal number of objects, but this will be inconvenient for users and difficult to maintain. I would be grateful for the advice.
asked
2 answers
0

One thing you could do is automatically split out the name into two separate attributes after the user saves the name. That way, nothing changes for the user. Use regex to find the first non-number character and then put both separate parts into different attributes. Sorting by the number attribute first and then the string might still not work as empty is usually grouped together, but you could at least build custom logic to sort as needed.

answered
0

In case you really want natural sorting (and this is the ordering you want):

  1. Create a Java action
    1. Give it a list as input
    2. Give it an attribute name to sort on
    3. Let it return a (sorted) list
  2. In the java action, create an override for the sort comparator (to allow for custom sorting)
  3. Return the sorted list

image.png

External libraries needed:

none

 

Used imports:

import com.mendix.systemwideinterfaces.core.IContext;

import com.mendix.webui.CustomJavaAction;

import com.mendix.systemwideinterfaces.core.IMendixObject;

import java.util.Arrays;

import java.util.Comparator;

import java.util.List;

import com.mendix.core.Core;

import com.mendix.logging.ILogNode;

 

Java code (based on examples from https://stackoverflow.com/questions/104599/sort-on-a-string-that-may-contain-a-number; you can use whatever override you want though, below is just one example):

// BEGIN USER CODE

ILogNode logger = Core.getLogger( "NaturalSort" );

if(Input == null) {

  logger.debug("Empty list");

  return Input;

}

//assume at least 1 entry in list

if(!Input.get(0).hasMember(SortingAttribute)){

  throw new com.mendix.systemwideinterfaces.MendixRuntimeException("Member does not exist in list: "+SortingAttribute);

}

//member exists at this point

if(Input.get(0).getValue(getContext(),SortingAttribute) instanceof String == false){       

  throw new com.mendix.systemwideinterfaces.MendixRuntimeException("Member " + SortingAttribute + " is not of type String.");     

}

//member is of type String at this point

logger.debug("Original list:");

Input.forEach(mxobj -> {String val = mxobj.getValue(getContext(),SortingAttribute);logger.debug(val);});

Input.sort(new Comparator() {

  @Override

  public int compare(IMendixObject mxobja, IMendixObject mxobjb) {

  String a = mxobja.getValue(getContext(),SortingAttribute);

  String b = mxobjb.getValue(getContext(),SortingAttribute);

  int la = a.length();

  int lb = b.length();

  int ka = 0;

  int kb = 0;

while (true) {

if (ka == la) return kb == lb ? 0 : -1;

if (kb == lb) return 1;

if (a.charAt(ka) >= '0' && a.charAt(ka) <= '9' && b.charAt(kb) >= '0' && b.charAt(kb) <= '9') {

int na = 0;

int nb = 0;

while (ka < la && a.charAt(ka) == '0')

ka++;

while (ka + na < la && a.charAt(ka + na) >= '0' && a.charAt(ka + na) <= '9')

na++;

while (kb < lb && b.charAt(kb) == '0')

kb++;

while (kb + nb < lb && b.charAt(kb + nb) >= '0' && b.charAt(kb + nb) <= '9')

nb++;

if (na > nb)

return 1;

if (nb > na)

return -1;

if (ka == la)

return kb == lb ? 0 : -1;

if (kb == lb)

return 1;

 

}

if (a.charAt(ka) != b.charAt(kb))

return a.charAt(ka) - b.charAt(kb);

ka++;

kb++;

}

}

});

logger.debug("Custom natural order sort:");

Input.forEach(mxobj -> {String val = mxobj.getValue(getContext(),SortingAttribute);logger.debug(val);});

return Input;

// END USER CODE

answered