tao-test/app/tao/helpers/class.Array.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]);
}
}