* @package tao */ class tao_helpers_Array { /** * Sort an associative array on a key criterion. Uses sort or asort PHP * functions to implement the sort depending on the value of the descending * parameter. * * @author Bertrand Chevrier, * @param array input The associative array to sort. * @param string field The key criterion. * @param boolean (optional, default = Ascending sort) descending Descending or Ascending order. * @return array An associative array. */ public static function sortByField($input, $field, $descending = false) { $returnValue = []; $sorted = []; foreach ($input as $key => $value) { $sorted[$key] = $value[$field]; } if ($descending) { arsort($sorted); } else { asort($sorted); } foreach ($sorted as $key => $value) { $returnValue[$key] = $input[$key]; } return (array) $returnValue; } /** * remove duplicate from array of objects implementing the __equal() function * * @param array $array * @return array $array */ public static function array_unique($array) { $keys = array_keys($array); $toDrop = []; for ($i = count($keys) - 1; $i >= 0; $i--) { for ($j = $i - 1; $j >= 0; $j--) { if ($array[$keys[$i]]->__equals($array[$keys[$j]])) { $toDrop[] = $keys[$i]; break; } } } foreach ($toDrop as $key) { unset($array[$key]); } return $array; } /** * Test if ann array is associative or not * * taken from http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential * * @param array $arr * @return boolean */ public static function isAssoc($arr) { return array_keys($arr) !== range(0, count($arr) - 1); } /** * Does an array contains only a given value. * * Whether or not a given array contains only a given value. * * * // Example 1 * $container = [1, 1, 1]; $value = 1; // true * $container = [1, 1, 2]; $value = 1; // false * * * When the $strict parameter is false, values contained in the $container array * will be compared with $value using the PHP == operator. Otherwise, the comparison * will be proceed with the PHP === operator. * * Some particular indexes of $container can be ignored with the help of the $exceptAtIndex parameter. * * * // Example 2 * $container = [1, 1, 2]; $value = 1; $exceptAtIndex = [2]; // true * $container = [1, 1, 2]; $value = 1; $exceptAtIndex = [1]; // false * * * * When $value is not a scalar value, the method returns false. * * When $container is empty, the method returns false. * * @param mixed $value * @param array $container * @param boolean $strict * @param array $exceptAtIndex * @return boolean */ public static function containsOnlyValue($value, array $container, $strict = false, $exceptAtIndex = []) { if (!is_scalar($value)) { return false; } if (empty($container)) { return false; } $matchCount = 0; foreach ($container as $key => $val) { if (in_array($key, $exceptAtIndex, true)) { continue; } $match = ($strict === false) ? $value == $val : $value === $val; if (!$match) { return false; } else { $matchCount++; } } return $matchCount !== 0; } /** * Does a collection of arrays contain only a given value. * * Whether or not a given collection of arrays contain only a given $value. * * * You can specify that some indexes of contained arrays do not have to match $value by setting the $exceptAtIndex array with the indexes to be ignored. * * When $exceptNContainers > 0, it is allowed that $containers contains $exceptNContainers arrays not matching $value. * * The $invalidContainers parameter is an optional reference that will be filled with the index of arrays from $containers that do not contain the $value value only. * * The $validContainers parameter is an optional reference that will be filled with the index of array from $containers that do contain the $value value only. * * @param array $containers * @param mixed $value * @param integer $exceptNContainers * @param array $exceptAtIndex * @param array $invalidContainers * @param array $validContainers * @return boolean */ public static function arraysContainOnlyValue(array $containers, $value, $exceptNContainers = 0, array $exceptAtIndex = [], array &$invalidContainers = [], array &$validContainers = []) { if ($exceptNContainers < 0) { $exceptNContainers = 0; } else { $exceptNContainers = intval($exceptNContainers); } $validCount = 0; $rowCount = 0; $expectedValidCount = count($containers) - intval($exceptNContainers); foreach ($containers as $row) { if (!is_array($row)) { return false; } $valid = true; $exceptCount = 0; for ($i = 0; $i < count($row); $i++) { if (in_array($i, $exceptAtIndex, true)) { $exceptCount++; continue; } elseif ($row[$i] !== $value) { $valid = false; break; } } if ($exceptCount !== 0 && $exceptCount === count($row)) { $valid = false; } if ($valid == true) { $validContainers[] = $rowCount; $validCount++; } else { $invalidContainers[] = $rowCount; } $rowCount++; } return $validCount === $expectedValidCount; } /** * Detect Row with Minimum of Value(s) * * This method helps you to dectect which is the array contained in $arrays containing * the less amount of specific value(s). * * Pleae note that the detection comparison is NOT strict (using the == PHP operator). * * @param mixed $values Can be either scalar or array. * @param array $arrays An array of arrays. * @param boolean $returnAll Wheter or not return an array of keys when some amounts of specific values are similar accross $arrays. * @return mixed Key(s) of the array(s) containing the less amount of $values. or false if it not possible to designate a row. */ public static function minArrayCountValues($values, array $arrays, $returnAll = false) { if (!is_array($values)) { $values = [$values]; } $counts = []; foreach ($arrays as $index => $arr) { $counts[$index] = 0; if (!is_array($arr)) { return false; } $arrayCountValues = array_count_values($arr); foreach ($values as $value) { $keys = array_keys($arrayCountValues); if (($search = array_search($value, $keys)) !== false) { $counts[$index] += $arrayCountValues[$keys[$search]]; } } } if (count($counts) > 0) { $mins = array_keys($counts, min($counts)); return ($returnAll) ? $mins : $mins[0]; } else { return false; } } /** * Count the Amount of Consistent Columns in Matrix * * This method aims at counting the number of columns in a $matrix containing strictly similar values. * * @param array $matrix An array containing exclusively arrays. * @param array $ignoreValues An array of values to be ignored while comparing values within columns. * @param array $emptyIsConsistent (optional) Consider empty columns (after ignoring values) as consistent. * @return mixed The amount of consistent columns in $matrix or false if $matrix is not a well formed matrix. */ public static function countConsistentColumns(array $matrix, array $ignoreValues = [], $emptyIsConsistent = false) { $consistentCount = 0; if (!self::isValidMatrix($matrix)) { return $consistentCount; } for ($i = 0; $i < count($matrix[0]); $i++) { $column = array_column($matrix, $i); if (count($column) !== count($matrix)) { // Malformed matrix. return false; } $column = array_unique($column); foreach ($ignoreValues as $ignoreVal) { if (($search = array_search($ignoreVal, $column, true)) !== false) { unset($column[$search]); } } if (count($column) === 1) { $consistentCount++; } elseif (count($column) === 0 && $emptyIsConsistent) { $consistentCount++; } } return $consistentCount; } /** * Checks if provided matrix satisfies requirements. * - as of PHP 7.2 count() expect array or countable object and will emit an E_WARNING in other cases. * * @param array $matrix * @return bool */ private static function isValidMatrix(array $matrix) { return is_array($matrix) && count($matrix) > 0 && is_array($matrix[0]); } }