Created
November 7, 2023 00:51
-
-
Save westc/4ce41e95d0aa0864dcaa7fec4ea3c162 to your computer and use it in GitHub Desktop.
Apex - Makes it easier to sort an array of values by allowing you to specify 1 or more key values for each array value.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Makes it easier to sort an array of values by allowing you to specify 1 or | |
* more key values for each array value. | |
*/ | |
public class KeySorter { | |
/** | |
* Represents a key that will be used to sort a value passed into `KeySorter`. | |
* This class is only addressable internally. | |
*/ | |
private class ComparableKey implements Comparable { | |
/** | |
* The different parts of the key. This should only be an array of | |
* numbers or strings. | |
*/ | |
public Object[] keyParts; | |
/** | |
* An array corresponding to `keyParts` indicating the type of each key | |
* part (Decimal or String). | |
*/ | |
public String[] keyPartTypes = new String[0]; | |
/** | |
* The index of the key's corresponding value. This is used to help | |
* with sorting. | |
*/ | |
public Integer index; | |
/** | |
* Constructor for defining the comparable key. | |
*/ | |
public ComparableKey(Object key, Integer index) { | |
keyParts = key instanceof Object[] ? (Object[])key : new Object[]{key}; | |
for (Object keyPart : keyParts) { | |
keyPartTypes.add( | |
(keyPart instanceof Integer || keyPart instanceof Double || keyPart instanceof Decimal) | |
? 'Decimal' | |
: 'String' | |
); | |
} | |
this.index = index; | |
} | |
/** | |
* Function called internally when calling `sort()` on a list of | |
* `ComparableKey` objects. | |
*/ | |
public Integer compareTo(Object compareTo) { | |
Object[] keyPartsB = ((ComparableKey)compareTo).keyParts; | |
String[] keyPartTypesB = ((ComparableKey)compareTo).keyPartTypes; | |
Integer aLen = keyParts.size(), bLen = keyPartsB.size(); | |
for (Integer i = 0, l = Math.min(aLen, bLen); i < l; i++) { | |
Object a = keyParts[i]; | |
Object b = keyPartsB[i]; | |
String aType = keyPartTypes[i]; | |
String bType = keyPartTypesB[i]; | |
if (a != b) { | |
if (aType == 'Decimal' && bType == 'Decimal') { | |
Decimal decA = (Decimal)a; | |
Decimal decB = (Decimal)b; | |
return decA < decB ? -1 : 1; | |
} | |
String strA = a == null ? '' : String.valueOf(a); | |
String strB = b == null ? '' : String.valueOf(b); | |
return strA < strB ? -1 : 1; | |
} | |
} | |
return aLen < bLen ? -1 : aLen > bLen ? 1 : 0; | |
} | |
} | |
/** | |
* Keys whose `index` property corresponding to the Keysorter's `values`. | |
*/ | |
private ComparableKey[] keys; | |
/** | |
* The index of the current `key` and `value`. | |
*/ | |
private Integer index; | |
/** | |
* The current values. If `sort()` has been called this will be an array of | |
* sorted values. | |
*/ | |
public Object[] values { get; private set; } | |
/** | |
* The current value that corresponds to the `key` that can be set or | |
* retrieved. | |
*/ | |
public Object value { | |
get { return values[index]; } | |
} | |
/** | |
* The key value(s) that will be used to determine the sort order of the | |
* current `value`. | |
*/ | |
public Object key { | |
set { | |
keys[index] = new ComparableKey(value, index); | |
} | |
get { return keys[index].keyParts; } | |
} | |
/** | |
* Constructor for starting off the KeySorter. | |
*/ | |
public KeySorter(Object[] values) { | |
this.values = values.clone(); | |
reset(); | |
} | |
/** | |
* Resets the keys and the index. | |
*/ | |
public void reset() { | |
index = -1; | |
Integer i = values.size(); | |
keys = new ComparableKey[i]; | |
while (0 < i--) { | |
keys[i] = new ComparableKey(null, i); | |
} | |
} | |
/** | |
* Determines if there is another `key` and `value` after the current one. | |
*/ | |
public Boolean hasNext() { | |
return index + 1 < values.size(); | |
} | |
/** | |
* If `hasNext()` results in `true` this will advance you to being able to | |
* retrieve the next `value` and set/get the next `key`. This will return | |
* what would have bee returned if `hasNext()` was called. | |
*/ | |
public Boolean next() { | |
Boolean result = this.hasNext(); | |
if (result) index++; | |
return result; | |
} | |
/** | |
* Sorts the values by using the keys that were specified in ascending | |
* order. | |
*/ | |
public Object[] sort() { | |
return sort(false); | |
} | |
/** | |
* Sorts the values by using the keys that were specified. If `reverse` is | |
* `true` the values will be sorted in descending order, otherwise it will | |
* be sorted in ascending order. The return value will be the sorted object | |
* array. | |
*/ | |
public Object[] sort(Boolean reverse) { | |
keys.sort(); | |
Object[] newValues = new Object[0]; | |
for (ComparableKey key : keys) { | |
if (reverse && !newValues.isEmpty()) { | |
newValues.add(0, values[key.index]); | |
} | |
else { | |
newValues.add(values[key.index]); | |
} | |
} | |
return newValues; | |
} | |
/** | |
* Returns an inverted version of the input string so that you can invert | |
* how a string column gets sorted. | |
*/ | |
public static String invert(String value) { | |
if (value == null) return value; | |
Integer[] ints = new Integer[0]; | |
for (Integer i = 0, l = value.length(); i < l; i++) { | |
ints.add(65536 - value.charAt(i)); | |
} | |
return String.fromCharArray(ints); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Gets all of the accounts. | |
KeySorter ksAccts = new KeySorter([ | |
SELECT Id, FirstName, LastName | |
FROM Account | |
WHERE FirstName != null | |
AND LastName != null | |
LIMIT 5 | |
]); | |
// Establishes the key to be used to sort each account and then sorts them | |
// (FirstName ASC, LastName DESC). | |
while (ksAccts.next()) { | |
Account acct = (Account)ksAccts.value; | |
ksAccts.key = new Object[]{acct.FirstName, KeySorter.invert(acct.LastName)}; | |
} | |
Object[] sorted1 = ksAccts.sort(); | |
System.debug(JSON.serializePretty(sorted1)); | |
// Establishes the key to be used to sort each account and then sorts them | |
// (FirstName DESC, LastName DESC). | |
ksAccts.reset(); | |
while (ksAccts.next()) { | |
Account acct = (Account)ksAccts.value; | |
ksAccts.key = new Object[]{acct.FirstName, acct.LastName}; | |
} | |
System.debug(JSON.serializePretty(ksAccts.sort(true))); | |
// Sort all of the words from longest to shortest and in alphabetical order. | |
KeySorter ksWords = new KeySorter('Where in the world is Carmen Sandiego?'.split('\\s+')); | |
while (ksWords.next()) { | |
ksWords.key = new Object[] { | |
-((String)ksWords.value).length(), | |
ksWords.value | |
}; | |
} | |
System.debug(ksWords.sort()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment