sort.php 4.94 KB
Newer Older
Scott committed
1 2 3 4 5
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

Scott committed
6
	File: qa-include/util/sort.php
Scott committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
	Description: A useful general-purpose 'sort by' function


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

Scott committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
	header('Location: ../');
	exit;
}

/**
 * Sort the $array of inner arrays by sub-element $by1 of each inner array, and optionally then by sub-element $by2
 */
function qa_sort_by(&$array, $by1, $by2 = null)
{
	global $qa_sort_by_1, $qa_sort_by_2;

	$qa_sort_by_1 = $by1;
	$qa_sort_by_2 = $by2;

	uasort($array, 'qa_sort_by_fn');
}


/**
 * Function used in uasort to implement qa_sort_by()
 */
function qa_sort_by_fn($a, $b)
{
	global $qa_sort_by_1, $qa_sort_by_2;

Scott committed
49 50 51 52 53
	// if the first keys are equal we can sort by the second keys only
	$sortkey = $qa_sort_by_1;
	if ($a[$sortkey] == $b[$sortkey]) {
		if (!isset($qa_sort_by_2))
			return 0;
Scott committed
54

Scott committed
55 56
		$sortkey = $qa_sort_by_2;
	}
Scott committed
57

Scott committed
58 59 60 61 62 63 64
	$av = $a[$sortkey];
	$bv = $b[$sortkey];

	if (is_numeric($av) && is_numeric($bv)) // straight subtraction won't work for floating bits
		return $av == $bv ? 0 : ($av < $bv ? -1 : 1);
	else
		return strcasecmp($av, $bv); // doesn't do UTF-8 right but it will do for now
Scott committed
65 66 67 68
}

/**
 * General comparison function for two values, textual or numeric
Scott committed
69
 * @deprecated
Scott committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
 */
function qa_sort_cmp($a, $b)
{
	if (is_numeric($a) && is_numeric($b)) // straight subtraction won't work for floating bits
		return $a == $b ? 0 : ($a < $b ? -1 : 1);
	else
		return strcasecmp($a, $b); // doesn't do UTF-8 right but it will do for now
}


/**
 * Inserts $addelements into $array, preserving their keys, before $beforekey in that array.
 * If $beforekey cannot be found, the elements are appended at the end of the array.
 */
function qa_array_insert(&$array, $beforekey, $addelements)
{
	$newarray = array();
	$beforefound = false;

	foreach ($array as $key => $element) {
		if ($key == $beforekey) {
			$beforefound = true;
Scott committed
92 93

			foreach ($addelements as $addkey => $addelement)
Scott committed
94 95
				$newarray[$addkey] = $addelement;
		}
Scott committed
96

Scott committed
97
		$newarray[$key] = $element;
Scott committed
98 99
	}

Scott committed
100 101 102 103
	if (!$beforefound) {
		foreach ($addelements as $addkey => $addelement)
			$newarray[$addkey] = $addelement;
	}
Scott committed
104

Scott committed
105 106
	$array = $newarray;
}
Scott committed
107 108


Scott committed
109
// Special values for the $beforekey parameter for qa_array_reorder() - use floats since these cannot be real keys
Scott committed
110

Scott committed
111 112 113 114
define('QA_ARRAY_WITH_FIRST', null); // collect the elements together in the position of the first one found
define('QA_ARRAY_WITH_LAST', 0.6); // collect the elements together in the position of the last one found
define('QA_ARRAY_AT_START', 0.1); // place all the elements at the start of the array
define('QA_ARRAY_AT_END', 0.9); // place all the elements at the end of the array
Scott committed
115

Scott committed
116 117 118 119 120 121 122 123
/**
 * Moves all of the elements in $array whose keys are in the parameter $keys. They can be moved to before a specific
 * element by passing the key of that element in $beforekey (if $beforekey is not found, the elements are moved to the
 * end of the array). Any of the QA_ARRAY_* values defined above can also be passed in the $beforekey parameter.
 * If $reorderrelative is true, the relative ordering between the elements will also be set by the order in $keys.
 */
function qa_array_reorder(&$array, $keys, $beforekey = null, $reorderrelative = true)
{
Scott committed
124

Scott committed
125
	// Make a map for checking each key in $array against $keys and which gives their ordering
Scott committed
126

Scott committed
127 128 129 130
	$keyorder = array();
	$keyindex = 0;
	foreach ($keys as $key)
		$keyorder[$key] = ++$keyindex;
Scott committed
131

Scott committed
132
	// Create the new key ordering in $newkeys
Scott committed
133

Scott committed
134 135 136
	$newkeys = array();
	$insertkeys = array();
	$offset = null;
Scott committed
137

Scott committed
138 139
	if ($beforekey == QA_ARRAY_AT_START)
		$offset = 0;
Scott committed
140

Scott committed
141 142 143
	foreach ($array as $key => $value) {
		if ($beforekey == $key)
			$offset = count($newkeys);
Scott committed
144

Scott committed
145 146 147 148 149
		if (isset($keyorder[$key])) {
			if ($reorderrelative)
				$insertkeys[$keyorder[$key]] = $key; // in order of $keys parameter
			else
				$insertkeys[] = $key; // in order of original array
Scott committed
150

Scott committed
151 152
			if ($beforekey == QA_ARRAY_WITH_LAST || ($beforekey === QA_ARRAY_WITH_FIRST && !isset($offset)))
				$offset = count($newkeys);
Scott committed
153

Scott committed
154 155 156
		} else
			$newkeys[] = $key;
	}
Scott committed
157

Scott committed
158 159
	if (!isset($offset)) // also good for QA_ARRAY_AT_END
		$offset = count($newkeys);
Scott committed
160

Scott committed
161 162
	if ($reorderrelative)
		ksort($insertkeys, SORT_NUMERIC); // sort them based on position in $keys parameter
Scott committed
163

Scott committed
164
	array_splice($newkeys, $offset, 0, $insertkeys);
Scott committed
165

Scott committed
166
	// Rebuild the array based on the new key ordering
Scott committed
167

Scott committed
168
	$newarray = array();
Scott committed
169

Scott committed
170 171
	foreach ($newkeys as $key)
		$newarray[$key] = $array[$key];
Scott committed
172

Scott committed
173 174
	$array = $newarray;
}