How to flip elements with numeric key in php? - php

I've an array in php like bellow:
array:2 [
"element_a" => array:1 [
"string" => array:3 [
"min" => 5
"max" => 50
0 => "nullable" // this element need be flipped
]
]
"element_b" => array:1 [
0 => "string" // this need too
]
]
And I need flip elements with numeric key value.. I want get the following output:
array:2 [
"element_a" => array:1 [
"string" => array:3 [
"min" => 5
"max" => 50
"nullable" => true
]
]
"element_b" => array:1 [
"string" => true
]
]
Any ideas about how i can get it?
Thanks.
Edit
I tried this, but no results:
array_walk_recursive($array, function (&$value, &$key) {
if (is_numeric($key)) {
list($value, $key) = [$key, $value];
}
});

This solution will fix your array in place (by reference):
function fix( array &$array ) {
foreach( $array as $key => &$value ) {
if( is_array( $value ) ) {
fix( $value );
}
else if( is_numeric( $key ) ) {
$array[ $value ] = true;
unset( $array[ $key ] );
}
}
}
$yourArray = [
"element_a" => [
"string" => [
"min" => 5,
"max" => 50,
0 => "nullable" // this element need be flipped
]
],
"element_b" => [
0 => "string" // this need too
]
];
fix( $yourArray );
var_dump( $yourArray );

Related

Getting zero when extracting data from a PHP array in Laravel app

Am working on some set of PHP array. Am trying to loop through each of them and check
the array whose name is equal to Josw Acade. Am using a for loop but I get zero
after extracting the data. I want to store the data in an array.
Array
array:6 [
0 => array:4 [
"id" => 1
"name" => "Josw Acade"
"value" => "Unlimited"
"plan_type" => "Superior"
]
1 => array:4 [
"id" => 2
"name" => "Verbal"
"value" => "true"
"plan_type" => "Superior"
]
2 => array:4 [
"id" => 12
"name" => "Josw Acade"
"value" => "$1,500,00"
"plan_type" => "Classic"
]
3 => array:4 [
"id" => 13
"name" => "Leon"
"value" => "true"
"plan_type" => "Classic"
]
4 => array:4 [
"id" => 14
"name" => "One Time"
"value" => "true"
"plan_type" => "Classic"
]
5 => array:4 [
"id" => 15
"name" => "Deat"
"value" => "$25,000"
"plan_type" => "Classic"
]
6 => array:4 [
"id" => 23
"name" => "Josw Acade"
"value" => "$100,000"
"plan_type" => "Essential"
]
]
Logic
$Inst = [];
for($med = 0; $med < count($array); $med++){
if($med['name'] == "Josw Acade"){
$Inst = $med['value'];
}
}
dd($Inst);
Your variables is not corretly set in the for loop, you are setting $med = 0 and acessing $med as an array.
Use filter, that runs a condition on each element and returns the items that satisfy that condition.
array_filter($array, function ($item) {
return $item['name'] === 'Josw Acade';
});
In general you don't have to make old school arrays anymore, foreach does the same.
$results = [];
foreach($array as $item)
{
if ($item['name'] === 'Josw Acade') {
$results[] = $item['value'];
}
}
You can use array_filter with callback
$filtered = array_filter($array, function($v){ return $v['name'] == 'Josw Acade'})
print_r($filtered);
You are looping through array; so on each iteration to get values; you need to pass index value and you are missing that. You are using $med as index.
Here is code.
$Inst = [];
for($med = 0; $med < count($array); $med++){
if($array[$med]['name'] == "Josw Acade"){
$Inst[] = $array[$med]['value'];
}
}
there is many way to do this but according to me the best way to use array_filer()
array_filter($array, function ($item) {
return $item['name'] === 'Josw Acade';
});

How to recursively sort associative array

I'd like to sort the following associative array:
$tree = [
"id" => 245974,
"children" => [
[
"id" => 111
],
[
"id" => 245982,
"children" => [
[
"id" => 246093,
"children" => [
[
"id" => 225892
],
[
"id" => 225893
],
[
"id" => 225902
]
]
]
]
]
]
];
Desired sort order after the "search value" of id => 225902:
[
"id" => 245974,
"children" => [
[
"id" => 245982, // <-- this is moved up
"children" => [
[
"id" => 246093,
"children" => [
[
"id" => 225902 // <-- this is moved up
],
[
"id" => 225892
],
[
"id" => 225893
]
]
]
]
],
[
"id" => 111
]
]
];
What I've tried:
<?php
$category_id = 225902;
function custom_sort(&$a, &$b) {
global $category_id;
if ($a['id'] === $category_id) {
return -1;
}
if ($b['id'] === $category_id) {
return 1;
}
if (array_key_exists('children', $a)) {
if (usort($a['children'], "custom_sort")) {
return -1;
}
}
if (array_key_exists('children', $b)) {
if (usort($b['children'], "custom_sort")) {
return 1;
}
}
return 0;
}
function reorder_tree($tree) {
usort($tree['children'], "custom_sort");
return $tree;
}
echo "<pre>";
var_dump(reorder_tree($tree));
echo "</pre>";
However, that returns:
[
"id" => 245974,
"children" => [
[
"id" => 245982, // <- this is moved up
"children" => [
[
"id" => 246093,
"children" => [
[
"id" => 225892
],
[
"id" => 225893
],
[
"id" => 225902 // <- this is *not* moved up
]
]
]
]
],
[
"id" => 111
],
]
];
How would I be able to also sort the children arrays?
Great attempt and very much on the right track. The problem with recursion in the comparator is that usort will not call the comparator function when the array length is 1, so whether or not you explore the whole tree is at the whim of usort. This will abandon id => 245982's branch of the tree.
The solution is to avoid recursing in the usort's comparator function directly. Rather, use a regular recursive function that calls usort as needed, namely, the current array or a child array contains the target id. I use a separate array to keep track of which elements should be moved forward, but you can break out of the loop and splice/unshift a single element to the front if you prefer.
We can also make $category_id a parameter to the function.
Here's one approach:
function reorder_tree_r(&$children, $target) {
$order = [];
$should_sort = false;
foreach ($children as $i => &$child) {
$order[$i] = false;
if (array_key_exists("children", $child) &&
reorder_tree_r($child["children"], $target) ||
$child["id"] === $target) {
$order[$i] = true;
$should_sort = true;
}
}
if ($should_sort) {
$priority = [];
$non_priority = [];
for ($i = 0; $i < count($children); $i++) {
if ($order[$i]) {
$priority[]= $children[$i];
}
else {
$non_priority[]= $children[$i];
}
}
$children = array_merge($priority, $non_priority);
}
return $should_sort;
}
function reorder_tree($tree, $target) {
if (!$tree || !array_key_exists("children", $tree)) {
return $tree;
}
reorder_tree_r($tree["children"], $target);
return $tree;
}
var_export(reorder_tree($tree, 225902));
Output:
array (
'id' => 245974,
'children' =>
array (
0 =>
array (
'id' => 245982,
'children' =>
array (
0 =>
array (
'id' => 246093,
'children' =>
array (
0 =>
array (
'id' => 225902,
),
1 =>
array (
'id' => 225892,
),
2 =>
array (
'id' => 225893,
),
),
),
),
),
1 =>
array (
'id' => 111,
),
),

How to filter arrays and objects by path while keeping the structure intact

I am developing a JSON API which is running on PHP 7.2. One of the API features is a basic "only show me these columns" filter. I would like to be able to make this dynamic without having to manually handle each "level" in the path.
My first approach was to only support a nested path five levels deep. This covers most use cases, and it has worked well for a few years.
/api/request/stuff?column=col1-sub1-data2,col1-sub2
Full structure (could be an object or an array, but I can cast to array if necessary):
[
'col1' => [
'sub1' => [
'data1' => false,
'data2' => 'abc',
'data3' => 123
],
'sub2' => [
'other1' => true,
'data3' => 987
]
],
'col2' => [
]
]
Here is an example of my very naive approach:
foreach ($columns as $column) {
$path = explode('-', $column);
if (!is_array($path) || empty($path)) {
continue;
}
$pathCount = count($path);
switch ($pathCount) {
case 0:
case 1:
/* Too shallow. These should be handled before this point anyway... */
continue 2;
case 2:
if (!isset($detailObject[ $path[ 0 ] ][ $path[ 1 ] ])
) {
continue 2;
}
$newArray[ $path[ 0 ] ][ $path[ 1 ] ] = $detailObject[ $path[ 0 ] ][ $path[ 1 ] ];
break;
case 3:
if (!isset($detailObject[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ])
) {
continue 2;
}
$newArray[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ] = $detailObject[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ];
break;
}
}
With the column parameter, the API processes the data and returns the following:
[
'col1' => [
'sub1' => [
'data2' => 'abc'
],
'sub2' => [
'other1' => true,
'data3' => 987
]
]
Is there a way to dynamically produce this output?
Performing operations on nested structures of arbitrary depth pretty much always calls for a recursive function.
function filterPath($data, $path) {
$key = array_shift($path);
if( empty($path) ) {
return [$key => $data[$key]];
} else {
return [$key => filterPath($data[$key], $path) ];
}
}
function filterPaths($data, $paths) {
$out = [];
foreach($paths as $path) {
$out = array_merge_recursive($out, filterPath($data, $path));
}
return $out;
}
$data = [
'col1' => [
'sub1' => [
'data1' => false,
'data2' => 'abc',
'data3' => 123
],
'sub2' => [
'other1' => true,
'data3' => 987
]
],
'col2' => [
]
];
var_dump(
filterPaths($data, [
['col1', 'sub1', 'data1'],
['col1', 'sub1', 'data3'],
['col1', 'sub2'],
['col2']
])
);
Output:
array(2) {
["col1"]=> array(2) {
["sub1"]=> array(2) {
["data1"]=> bool(false)
["data3"]=> int(123)
}
["sub2"]=> array(2) {
["other1"]=> bool(true)
["data3"]=> int(987)
}
}
["col2"]=> array(0) {}
}

How map array of array to single assoc array?

I have:
array:2 [
0 => array:1 [
"FNAME" => "nullable|string"
]
1 => array:1 [
"LNAME" => "nullable|string"
]
]
And I try to get:
array:1 [
"key" => "value"
]
I try map it, but has problem
<?php
$array = [
[
"FNAME" => "nullable|string",
],
[
"LNAME" => "nullable|string",
]
];
$newArray = [];
foreach ($array as $item) {
foreach ($item as $key => $value) {
$newArray[$key] = $value;
}
}
print_r($newArray);
Will output:
Array
(
[FNAME] => nullable|string
[LNAME] => nullable|string
)
Two simple ways:
print_r(array_merge(...$arr));
// if `...` is not available (php < 5.6), then:
print_r(call_user_func_array('array_merge', $arr))

How to navigate back up in a multidimensional array after finding the appropriate child?

I have an array that looks like this:
RecursiveArrayIterator {#605 ▼
+"xs:schema": array:2 [▼
"value" => array:1 [▼
"xs:element" => array:2 [▼
"value" => array:1 [▼
"xs:complexType" => array:2 [▼
"value" => array:2 [▼
"xs:sequence" => array:2 [▼
"value" => array:1 [▼
"xs:element" => array:3 [▼
0 => array:2 [▼
"value" => array:1 [▼
"xs:simpleType" => array:2 [▼
"value" => array:1 [▼
"xs:restriction" => array:2 [▼
"value" => array:1 [▼
"xs:maxLength" => array:1 [▼
"attributes" => array:1 [▼
"value" => "40"
]
]
]
"attributes" => array:1 [▶]
]
]
"attributes" => []
]
]
"attributes" => array:1 [▼
"name" => "title"
]
]
1 => array:2 [▶]
2 => array:2 [▶]
]
]
"attributes" => []
]
"xs:attribute" => array:2 [▶]
]
"attributes" => []
]
]
"attributes" => array:1 [▼
"name" => "book"
]
]
]
"attributes" => []
]
}
I need to access the xs:maxLength attribute, so in order to that that, I'm using the following method:
private function findRestrictions(array $haystack, $needle)
{
$iterator = new \RecursiveArrayIterator($haystack);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($recursive as $key => $value)
{
if ($key === $needle)
{
return (int)$value['attributes']['value'];
}
}
}
$maxLength = findRestrictions($array, 'xs:maxLength');
So that gives me back 40, just like expected. Anyway, my issue is that I need to know to which element this limit belongs to, which is mentioned in xs:element[0]['attributes']['name'] and I'm uncertain on how to reach there to grab the information I need, based on the match for xs:maxLength.
Well I have programmed a pretty good solution I think, this time it is tested.
My sample array:
$array = [
"we" =>
["are" => [
"lorem" => [
"gone" => "away",
"my" => "friend"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx"
]
]
]
],
"fancy" => [
"lorem" => [
"gone" => "away",
"my" => "friend"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx",
"abc" => 13
]
]
],
"beer" => "bar",
"helpful" => [
"meta" => "column",
"gamma" => [
"lorem" => [
"gone" => "mad",
"my" => "drink"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx",
"abc" => "alot"
]
]
]
],
"elements" => [
0 => 88,
1 => 99
]
];
My solution:
function array_find_value_return_parent($array,$needle,$parentkey) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $key => $value) {
if($value === $needle) {
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
if($iterator->getSubIterator($i)->key() === $parentkey) {
return $iterator->getSubIterator($i)->current();
}
}
}
}
}
function array_find_key_return_value($array,$findkey) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $key => $value) {
if($findkey === $key) {
return $iterator->current();
}
}
}
My test:
$findvalue = "alot";
$findparentkey = "gamma";
$findreturnkey = "gone";
echo array_find_key_return_value(array_find_value_return_parent($array,$findvalue,$findparentkey),$findreturnkey);
Output: mad
For your case it means that you might do the following:
$findvalue = "40";
$findparentkey = "xs:element";
$findreturnkey = "name";
echo array_find_key_return_value(array_find_value_return_parent($array,$findvalue,$findparentkey),$findreturnkey);
Expected output: title
Right?
I do not know your original data structure, so I just converted your data to a PHP array. You can use $aks = new ArrayKeySearcher($data, 'xs:maxLength'); to find the key you want. And you can make the search more complex to satisfy your requirement.
However, if you are using something like XML, it is highly recommended to use XML-based solutions, like XPath query (eg: http://php.net/manual/en/domxpath.query.php, http://php.net/manual/en/simplexmlelement.xpath.php). These methods are easier to use, faster and more accurate.
<?php
$data = [
"xs:schema"=> [
"value" => [
"xs:element" => [
"value" => [
"xs:complexType" => [
"value" => [
"xs:sequence" => [
"value" => [
"xs:element" => [
0 => [
"value" => [
"xs:simpleType" => [
"value" => [
"xs:restriction" => [
"value" => [
"xs:maxLength" => [
"attributes" => [
"value" => "40"
]
]
],
"attributes" => []
]
],
"attributes" => []
]
],
"attributes" => [
"name" => "title"
]
],
1 => [],
2 => [],
]
],
"attributes" => []
],
"xs:attribute" => []
],
"attributes" => []
]
],
"attributes" => [
"name" => "book"
]
]
],
"attributes" => []
]
];
class ArrayKeySearcher
{
public $data;
public $path;
public $value;
public function __construct($data, $key)
{
$this->data = $data;
$this->findKeyPath($data, $key);
}
private function findKeyPath($data, $key)
{
foreach ($data as $k => $v) {
$this->path[] = $k;
if ($key === $k) {
$this->value = $v;
return;
}
$this->findKeyPath($v, $key);
if (!is_null($this->value))
return;
array_pop($this->path);
}
}
public function arrayReverseSearch($a, $k, $pos = null)
{
$count = count($a);
$i = ($pos === null) ? ($count - 1) : $pos;
for(; $i >= 0; $i--) {
if($a[$i] === $k)
return $i;
}
return $i;
}
public function getValueByPath($path)
{
$v = $this->data;
foreach($path as $k) {
if(isset($v[$k]))
$v = $v[$k];
}
return $v;
}
}
$aks = new ArrayKeySearcher($data, 'xs:maxLength');
echo 'path: ' . json_encode($aks->path) . PHP_EOL;
echo 'value: ' . json_encode($aks->value) . PHP_EOL;
$p = $aks->path;
$pos = $aks->arrayReverseSearch($p, 'xs:simpleType');
$pos = $aks->arrayReverseSearch($p, 'value', $pos);
$p = array_slice($p, 0, $pos);
$parent = $aks->getValueByPath($p);
echo 'parent path: ' . json_encode($p) . PHP_EOL;
echo 'parent attributes: ' . json_encode($parent['attributes']) . PHP_EOL;
Output:
path: ["xs:schema","value","xs:element","value","xs:complexType","value","xs:sequence","value","xs:element",0,"value","xs:simpleType","value","xs:restriction","value","xs:maxLength"]
value: {"attributes":{"value":"40"}}
parent path: ["xs:schema","value","xs:element","value","xs:complexType","value","xs:sequence","value","xs:element",0]
parent attributes: {"name":"title"}

Categories