330 lines
11 KiB
PHP
330 lines
11 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* 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; under version 2
|
||
|
* of the License (non-upgradable).
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
* Copyright (c) 2008-2010 (original work) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
|
||
|
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
|
||
|
* 2013 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT);
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Utility class on Arrays.
|
||
|
*
|
||
|
* @author Bertrand Chevrier, <bertrand.chevrier@tudor.lu>
|
||
|
* @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, <bertrand.chevrier@tudor.lu>
|
||
|
* @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.
|
||
|
*
|
||
|
* <code>
|
||
|
* // Example 1
|
||
|
* $container = [1, 1, 1]; $value = 1; // true
|
||
|
* $container = [1, 1, 2]; $value = 1; // false
|
||
|
* </code>
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* <code>
|
||
|
* // Example 2
|
||
|
* $container = [1, 1, 2]; $value = 1; $exceptAtIndex = [2]; // true
|
||
|
* $container = [1, 1, 2]; $value = 1; $exceptAtIndex = [1]; // false
|
||
|
* </code>
|
||
|
*
|
||
|
* * 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]);
|
||
|
}
|
||
|
}
|