Add new funtions and update

- ksortArray and sortArray
Sort array and return sorted output in one flow. Allows for case insensitve sort, reverse sort

- selectArrayFromOption
select array blocks based on a "key": "value" match.
Can do recusrive with flat or not flat output, strict matching, case insenstivie
The flat combined character can be changed

- findArraysMissingKey
Search an array for a matching value with optional key match and return array block if in this block a key (or keys) are missing
The matching for key and value is always strict, eg 2 and '2' are different,
Found path is added with ":" separators, can be overridden by parameter

- arraySearchSimple
Allow search values to be array for multiple matching (any match)
This commit is contained in:
Clemens Schwaighofer
2025-06-05 13:26:18 +09:00
parent 4707427ff4
commit 3be3519e45
7 changed files with 2813 additions and 55 deletions

View File

@@ -51,6 +51,9 @@ $test_array = [
'element_c' => [
'type' => 'email'
],
'element_d' => [
'type' => 'butter'
],
],
];
@@ -70,7 +73,15 @@ echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
// simple search
echo "ARRAYSEARCHSIMPLE([array], type, email): "
. (string)ArrayHandler::arraySearchSimple($test_array, 'type', 'email') . "<br>";
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', 'email')) . "<br>";
echo "ARRAYSEARCHSIMPLE([array], type, not): "
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', 'not')) . "<br>";
echo "ARRAYSEARCHSIMPLE([array], type, [email,butter]): "
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['email', 'butter'])) . "<br>";
echo "ARRAYSEARCHSIMPLE([array], type, [email,not]): "
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['email', 'not'])) . "<br>";
echo "ARRAYSEARCHSIMPLE([array], type, [never,not]): "
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['never', 'not'])) . "<br>";
$array_1 = [
'foo' => 'bar'
@@ -292,6 +303,231 @@ print "array intersect key: " . DgS::printAr($keys) . ": " . DgS::printAr($out)
print "array + suffix: " . DgS::printAr(ArrayHandler::arrayModifyKey($array, key_mod_suffix:'_attached')) . "<br>";
print "<hr>";
$unsorted = [9, 5, 'A', 4, 'B', 6, 'c', 'C', 'a'];
$unsorted_keys = [
'A' => 9, 'B' => 5, 'C' => 'A', 'D' => 4, 'E' => 'B', 'F' => 6, 'G' => 'c',
'H1' => 'D', 'B1' => 'd', 'H' => 'C', 'I' => 'a'
];
print "Unsorted: " . DgS::printAr($unsorted) . "<br>";
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($unsorted)) . "<br>";
print "(sort, lower): " . DgS::printAr(ArrayHandler::sortArray($unsorted, case_insensitive:true)) . "<br>";
print "(sort, reverse): " . DgS::printAr(ArrayHandler::sortArray($unsorted, reverse:true)) . "<br>";
print "(sort, lower, reverse): "
. DgS::printAr(ArrayHandler::sortArray($unsorted, case_insensitive:true, reverse:true)) . "<br>";
print "(sort, keys): " . DgS::printAr(ArrayHandler::sortArray($unsorted_keys, maintain_keys:true)) . "<br>";
print "(sort, keys, lower): "
. DgS::printAr(ArrayHandler::sortArray($unsorted_keys, maintain_keys:true, case_insensitive:true)) . "<br>";
print "<hr>";
$unsorted = [9 => 'A', 5 => 'B', 'A' => 'C', 4 => 'D', 'B' => 'E', 6 => 'F', 'c' => 'G', 'C' => 'H', 'a' => 'I'];
print "Unsorted Keys: " . DgS::printAr($unsorted) . "<br>";
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($unsorted)) . "<br>";
print "(sort, keys): " . DgS::printAr(ArrayHandler::sortArray($unsorted, maintain_keys:true)) . "<br>";
print "(kosrt): " . DgS::printAr(ArrayHandler::ksortArray($unsorted)) . "<br>";
print "(kosrt, reverse): " . DgS::printAr(ArrayHandler::ksortArray($unsorted, reverse:true)) . "<br>";
print "(kosrt, lower case, reverse): "
. DgS::printAr(ArrayHandler::ksortArray($unsorted, case_insensitive:true, reverse:true)) . "<br>";
print "<hr>";
$nested = [
'B' => 'foo', 'a', '0', 9,
1 => ['z', 'b', 'a'],
'd' => ['zaip', 'bar', 'baz']
];
print "Nested: " . DgS::printAr($nested) . "<br>";
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($nested)) . "<br>";
print "(ksort): " . DgS::printAr(ArrayHandler::ksortArray($nested)) . "<br>";
print "<hr>";
$search_array = [
'table_lookup' => [
'match' => [
['param' => 'access_d_cd', 'data' => 'a_cd', 'time_validation' => 'on_load',],
['param' => 'other_block', 'data' => 'b_cd'],
['pflaume' => 'other_block', 'data' => 'c_cd'],
['param' => 'third_block', 'data' => 'd_cd', 'time_validation' => 'cool'],
['special' => 'other_block', 'data' => 'e_cd', 'time_validation' => 'other'],
]
]
];
print "Search: " . DgS::printAr($search_array) . "<br>";
print "Result (all): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
'time_validation'
)) . "<br>";
print "Result (key): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
'time_validation',
'pflaume'
)) . "<br>";
print "Result (key): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
['data', 'time_validation'],
'pflaume'
)) . "<br>";
print "<hr>";
$search_array = [
'a' => [
'lookup' => 1,
'value' => 'Foo',
'other' => 'Bar',
],
'b' => [
'lookup' => 1,
'value' => 'AAA',
'other' => 'Other',
],
'c' => [
'lookup' => 0,
'value' => 'CCC',
'other' => 'OTHER',
],
'd' => [
'd-1' => [
'lookup' => 1,
'value' => 'D SUB 1',
'other' => 'Other B',
],
'd-2' => [
'lookup' => 0,
'value' => 'D SUB 2',
'other' => 'Other C',
],
'more' => [
'lookup' => 1,
'd-more-1' => [
'lookup' => 1,
'value' => 'D MORE SUB 1',
'other' => 'Other C',
],
'd-more-2' => [
'lookup' => 0,
'value' => 'D MORE SUB 0',
'other' => 'Other C',
],
]
]
];
print "Search: " . DgS::printAr($search_array) . "<br>";
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
$search_array,
'lookup',
1,
)) . "<br>";
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
$search_array,
'lookup',
1,
recursive:true
)) . "<br>";
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
$search_array,
'lookup',
1,
recursive:true,
flat_separator:'-=-'
)) . "<br>";
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
$search_array,
'lookup',
1,
recursive:true,
flat_result:false
)) . "<br>";
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
$search_array,
'other',
'Other',
case_insensitive:false,
)) . "<br>";
$nestedTestData = [
'level1_a' => [
'name' => 'Level1A',
'type' => 'parent',
'children' => [
'child1' => [
'name' => 'Child1',
'type' => 'child',
'active' => true
],
'child2' => [
'name' => 'Child2',
'type' => 'child',
'active' => false
]
]
],
'level1_b' => [
'name' => 'Level1B',
'type' => 'parent',
'children' => [
'child3' => [
'name' => 'Child3',
'type' => 'child',
'active' => true,
'nested' => [
'deep1' => [
'name' => 'Deep1',
'type' => 'deep',
'active' => true
]
]
]
]
],
'item5' => [
'name' => 'Direct',
'type' => 'child',
'active' => false
]
];
$result = ArrayHandler::selectArrayFromOption(
$nestedTestData,
'type',
'child',
false,
false,
true,
true,
':*'
);
print "*1*Result: " . DgS::printAr($result) . "<br>";
$data = [
'parent1' => [
'name' => 'Parent1',
'status' => 'ACTIVE',
'children' => [
'child1' => [
'name' => 'Child1',
'status' => 'active'
]
]
]
];
$result = ArrayHandler::selectArrayFromOption(
$data,
'status',
'active',
false, // not strict
true, // case insensitive
true, // recursive
true, // flat result
'|' // custom separator
);
print "*2*Result: " . DgS::printAr($result) . "<br>";
print "</body></html>";
// __END__

View File

@@ -10,7 +10,7 @@ namespace CoreLibs\Combined;
class ArrayHandler
{
public const string DATA_SEPARATOR = '#';
public const string DATA_SEPARATOR = ':';
/**
* searches key = value in an array / array
@@ -151,7 +151,7 @@ class ArrayHandler
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int $key key (key to search in)
* @param string|int|bool|array<string|int|bool> $value values list (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
@@ -243,45 +243,73 @@ class ArrayHandler
}
/**
* TODO: move to CoreLibs
* Search in an array for value and check in the same array block for the required key
* Search in an array for value with or without key and
* check in the same array block for the required key
* If not found return an array with the array block there the required key is missing,
* the path as string with seperator block set and the missing key entry
*
* @param array<mixed> $array
* @param string|int|float|bool $search_value
* @param string $required_key
* @param string|array<string> $required_key
* @param ?string $serach_key [null]
* @param string $path_separator [DATA_SEPARATOR]
* @param string $current_path
* @return array<array{content?:array<mixed>,path?:string,missing_key?:string}>
*/
public static function findArraysMissingKey(
array $array,
string|int|float|bool $search_value,
string $required_key,
string|array $required_key,
?string $search_key = null,
string $path_separator = self::DATA_SEPARATOR,
string $current_path = ''
): array {
$results = [];
foreach ($array as $key => $value) {
$path = $current_path ? $current_path . self::DATA_SEPARATOR . $key : $key;
$path = $current_path ? $current_path . $path_separator . $key : $key;
if (is_array($value)) {
// Check if this array contains the search value
$containsValue = in_array($search_value, $value, true);
// either any value match or with key
if ($search_key === null) {
$containsValue = in_array($search_value, $value, true);
} else {
$containsValue = array_key_exists($search_key, $value) && $value[$search_key] === $search_value;
}
// If it contains the value but doesn't have the required key
if ($containsValue && !array_key_exists($required_key, $value)) {
if (
$containsValue &&
(
(
is_string($required_key) &&
!array_key_exists($required_key, $value)
) || (
is_array($required_key) &&
count(array_intersect($required_key, array_keys($value))) !== count($required_key)
)
)
) {
$results[] = [
'content' => $value,
'path' => $path,
'missing_key' => $required_key
'missing_key' => is_array($required_key) ?
array_values(array_diff($required_key, array_keys($value))) :
[$required_key]
];
}
// Recursively search nested arrays
$results = array_merge(
$results,
self::findArraysMissingKey($value, $search_value, $required_key, $path)
self::findArraysMissingKey(
$value,
$search_value,
$required_key,
$search_key,
$path_separator,
$path
)
);
}
}
@@ -290,15 +318,17 @@ class ArrayHandler
}
/**
* TODO: move to CoreLibs
* currenly only over one level, find key => value entry and return set with key
* for all matching
* Find key => value entry and return set with key for all matching
* Can search recursively through nested arrays if recursive flag is set
*
* @param array<mixed> $array
* @param string $lookup
* @param array<mixed> $array
* @param string $lookup
* @param int|string|float|bool $search
* @param bool $strict [default=false]
* @param bool $case_senstivie [default=false]
* @param bool $strict [false]
* @param bool $case_insensitive [false]
* @param bool $recursive [false]
* @param bool $flat_result [true] If set to false and recursive is on the result is a nested array
* @param string $flat_separator [DATA_SEPARATOR] if flat result is true, can be any string
* @return array<mixed>
*/
public static function selectArrayFromOption(
@@ -306,28 +336,63 @@ class ArrayHandler
string $lookup,
int|string|float|bool $search,
bool $strict = false,
bool $case_senstivie = false,
bool $case_insensitive = false,
bool $recursive = false,
bool $flat_result = true,
string $flat_separator = self::DATA_SEPARATOR
): array {
// skip on empty
if ($array == []) {
return [];
}
// init return result
$result = [];
if ($case_senstivie && is_string($search)) {
// case sensitive convert if string
if ($case_insensitive && is_string($search)) {
$search = strtolower($search);
}
foreach ($array as $key => $value) {
// skip on not set
if (!isset($value[$lookup])) {
continue;
// Handle current level search
if (isset($value[$lookup])) {
$compareValue = $value[$lookup];
if ($case_insensitive && is_string($compareValue)) {
$compareValue = strtolower($compareValue);
}
if (
($strict && $search === $compareValue) ||
(!$strict && $search == $compareValue)
) {
$result[$key] = $value;
}
}
if ($case_senstivie && is_string($value[$value])) {
$value[$lookup] = strtolower($value[$value]);
}
if (
($strict && $search === $value[$lookup]) ||
(!$strict && $search == $value[$lookup])
) {
$result[$key] = $value;
continue;
// Handle recursive search if flag is set
if ($recursive && is_array($value)) {
$recursiveResults = self::selectArrayFromOption(
$value,
$lookup,
$search,
$strict,
$case_insensitive,
true,
$flat_result,
$flat_separator
);
// Merge recursive results with current results
// Preserve keys by using array_merge with string keys or + operator
foreach ($recursiveResults as $recKey => $recValue) {
if ($flat_result) {
$result[$key . $flat_separator . $recKey] = $recValue;
} else {
$result[$key][$recKey] = $recValue;
}
}
}
}
return $result;
}
@@ -653,8 +718,8 @@ class ArrayHandler
* does not change order in array
*
* @param array<string|int,mixed> $in_array
* @param string $key_mod_prefix [default=''] key prefix string
* @param string $key_mod_suffix [default=''] key suffix string
* @param string $key_mod_prefix [''] key prefix string
* @param string $key_mod_suffix [''] key suffix string
* @return array<string|int,mixed>
*/
public static function arrayModifyKey(
@@ -680,35 +745,66 @@ class ArrayHandler
/**
* sort array and return in same call
* sort ascending, value
* sort ascending or descending with or without lower case convert
* value only, will loose key connections unless preserve_keys is set to true
*
* @param array<mixed> $array array to sort by values
* @param int $params sort flags
* @return array<mixed>
*/
public function sortArray(array $array, int $params = SORT_REGULAR): array
{
return sort($array, $params) ? $array : $array;
public static function sortArray(
array $array,
bool $case_insensitive = false,
bool $reverse = false,
bool $maintain_keys = false,
int $params = SORT_REGULAR
): array {
$fk_sort_lower_case = function (string $a, string $b): int {
return strtolower($a) <=> strtolower($b);
};
$fk_sort_lower_case_reverse = function (string $a, string $b): int {
return strtolower($b) <=> strtolower($a);
};
$case_insensitive ? (
$maintain_keys ?
(uasort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
(usort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
) :
(
$maintain_keys ?
($reverse ? arsort($array, $params) : asort($array, $params)) :
($reverse ? rsort($array, $params) : sort($array, $params))
);
return $array;
}
/**
* sort by key ascending and return
* sort by key ascending or descending and return
*
* @param array<mixed> $array
* @param bool $lower_case [default=false]
* @param bool $case_insensitive [false]
* @param bool $reverse [false]
* @return array<mixed>
*/
public static function ksortArray(array $array, bool $lower_case = false): array
public static function ksortArray(array $array, bool $case_insensitive = false, bool $reverse = false): array
{
$fk_sort_lower_case = function (string $a, string $b): int {
return strtolower($a) <=> strtolower($b);
};
$fk_sort_lower_case_reverse = function (string $a, string $b): int {
return strtolower($b) <=> strtolower($a);
};
$fk_sort = function (string $a, string $b): int {
return $a <=> $b;
};
$fk_sort_reverse = function (string $a, string $b): int {
return $b <=> $a;
};
uksort(
$array,
$lower_case ? $fk_sort_lower_case : $fk_sort
$case_insensitive ?
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
($reverse ? $fk_sort_reverse : $fk_sort)
);
return $array;
}