How could I optimise this PHP function? - php

Here I have a function where I delete some keys from a nested array.
How could I optimise it in order to look prettier and maybe more efficiently?
$data is array type and contains the following:
$data = [
'0' => [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3'
],
'1' => [
'key1' => 'value4',
'key2' => 'value5',
'key3' => 'value6'
],
];
And $column(s) is of type array and contains keys that I want to remove from this array of arrays
$columns = ['key1', 'key2'];
And a part of the function written in PHP is:
***
$newData = [];
foreach ($data as $row) {
$newRow = array_diff_key($row,array_flip($columns));
array_push($newData, $newRow);
}
return $newData;
***
Expected result:
$data = [
'0' => [
'key1' => 'value1'
],
'1' => [
'key1' => 'value4'
],
];
The function works well but it looks awful :D

I hope this solves your problem
function delKey ($data, $columns){
$result = array();
for ($i = 0; $i<count($data); $i++){
$a = $data["$i"];
foreach ($columns as $key){
if (isset($a[$key])){
unset($a[$key]);
}
}
$result["$i"] = $a;
}
return $result;
}

Related

PHP grouping content of multidimensional array with new structure

I usually use Eloquent so transposing the data is much easier. However i'm struggling to this in vanilla PHP.
I have tried array_map(null, ...$array) however get an error due to it not being an array.
I have got the following keyed array:
[
'email' => [
"william.pool#gmail.com",
"martynleeball#gmail.com"
],
'lastName' => [
'Pool',
'Ball'
],
'firstName' => [
'William',
'Martyn'
],
'id' => [
'j8zwyk',
'1'
]
]
I need to convert this to the following format:
[
0 => [
'email' => "william.pool#gmail.com",
'lastName' => 'Pool',
'firstName' => 'William',
'id' => 'j8zwyk'
],
1 => [
'email' => "martynleeball#gmail.com",
'lastName' => 'Ball',
'firstName' => 'Martyn',
'id' => '1'
]
]
Create new array with length 2 and loop through origin array. In loop insert relevant item into new array.
So if your array has only 2 item per key use
$newArr = [];
foreach($arr as $key=>$item){
$newArr[0][$key] = $item[0];
$newArr[1][$key] = $item[1];
}
But if it has unknown item use
$newArr = [];
foreach($arr as $key=>$item){
foreach($item as $key2=>$item2)
$newArr[$key2][$key] = $item2;
}
Check result in demo
$newArray = [];
foreach ($array as $key => $value) {
for ($i = 0; $i < count($value); $i++) {
$newArray[$i][$key] = $value[$i];
}
}

PHP array key exists from string

I have an array:
<?php
$array = [
'fruits' => [
'apple' => 'value',
'orange' => 'value'
],
'vegetables' => [
'onion' => 'value',
'carrot' => 'value'
];
I also have a string:
$string = 'fruits[orange]';
Is there any way to check if the - array key specified in the string - exists in the array?
For example:
<?php
if(array_key_exists($string, $array))
{
echo 'Orange exists';
}
Try this one. Here we are using foreach and isset function.
Note: This solution will also work for more deeper levels Ex: fruits[orange][x][y]
Try this code snippet here
<?php
ini_set('display_errors', 1);
$array = [
'fruits' => [
'apple' => 'value',
'orange' => 'value'
],
'vegetables' => [
'onion' => 'value',
'carrot' => 'value'
]
];
$string = 'fruits[orange]';
$keys=preg_split("/\[|\]/", $string, -1, PREG_SPLIT_NO_EMPTY);
echo nestedIsset($array,$keys);
function nestedIsset($array,$keys)
{
foreach($keys as $key)
{
if(array_key_exists($key,$array))://checking for a key
$array=$array[$key];
else:
return false;//returning false if any of the key is not set
endif;
}
return true;//returning true as all are set.
}
It would be a lot easier to check the other way around. As in check if the key is in the string. Since keys are unique, there's no way you have duplicates.
$array = [
'fruits' => [
'apple' => 'value',
'orange' => 'value'
],
'vegetables' => [
'onion' => 'value',
'carrot' => 'value'
]
];
$string = 'fruits[orange]';
$keys = array_keys($array['fruits']);
foreach($keys as $fruit) {
if(false !== stripos($string, $fruit)) {
return true;
}
}
While this solution is not necessarily ideal, the problem to begin with isn't exactly common.
You can walk recursively:
$array = [
'fruits' => [
'apple' => 'value',
'orange' => 'value'
],
'vegetables' => [
'onion' => 'value',
'carrot' => 'value'
]
];
$exists = false;
$search = "orange";
array_walk_recursive($array, function ($val, $key) use (&$exists,$search) {
if ($search === $key) { $exists = true; }
});
echo ($exists?"Exists":"Doesn't exist");
Prints:
Exists
Example: http://sandbox.onlinephpfunctions.com/code/a3ffe7df25037476979f4b988c2f36f35742c217
Instead of using regex or strpos like the other answers, you could also simply split your $string on [ and resolve the keys one by one until there's only one key left. Then use that last key in combination with array_key_exists() to check for your item.
This should work for any amount of dimensions (eg fruit[apple][value][1]).
Example:
<?php
$arr = [
'fruits' => [
'orange' => 'value'
]
];
// Resolve keys by splitting on '[' and removing ']' from the results
$keys = 'fruits[orange]';
$keys = explode("[", $keys);
$keys = array_map(function($s) {
return str_replace("]", "", $s);
}, $keys);
// Resolve item.
// Stop before the last key.
$item = $arr;
for($i = 0; $i < count($keys) - 1; $i++) {
$item = $item[$keys[$i]];
}
// Check if the last remaining key exists.
if(array_key_exists($keys[count($keys)-1], $item)) {
// do things
}
You can explode and check the indices of the array.
$array = array(
'fruits' => [
'apple' => 'value',
'orange' => 'value'
],
'vegetables' => [
'onion' => 'value',
'carrot' => 'value'
]);
$string = 'fruits[orange]';
$indexes = (preg_split( "/(\[|\])/", $string));
$first_index= $indexes[0];
$seconnd_index= $indexes[1];
if(isset($array[$first_index][$seconnd_index]))
{
echo "exist";
}
else
{
echo "not exist";
}
DEMO

PHP: Link address of multiple array keys into another bigger array

I want to combine 3 small arrays that have unique keys between them into 1 big array but when I modify a value in the big array I want it also to reflect in the corresponding small array.
For example I have these 3 small arrays:
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
I want to have a $bigArray that has each key's address linked/mapped to each value of the small arrays. So if I do something like:
$bigArray['key4'] = 'something else';
then it would modify $arr2['key4'] to the same value ('something else').
If I try something like:
$bigArray = [&$arr1, &$arr2, &$arr3];
It has the unfortunate effect of making a multidimensional array with the keys to the values mapped.
Two ways i found
<?php
error_reporting(E_ALL);
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
$big = [];
if (true) {
foreach (['arr1', 'arr2', 'arr3'] as $v) {
foreach (array_keys($$v) as $k) {
$big[$k] = &${$v}[$k];
}
}
}
else {
foreach ([&$arr1, &$arr2, &$arr3] as &$v) {
foreach (array_keys($v) as $k) {
$big[$k] = &$v[$k];
}
}
}
$big['key1'] = 'data1mod';
print_r($big);
print_r($arr1);
3rd way with function
$big = [];
$bindToBig = function (&$a) use (&$big) {
foreach (array_keys($a) as $k) {
$big[$k] = &$a[$k];
}
};
$bindToBig($arr1);
$bindToBig($arr2);
$bindToBig($arr3);
You can't bind data that way, but you can link them in the same object:
class ArrayLink {
public $bigArray;
public $linkedChildrenArray;
protected $childrenArray;
public function __construct( $childrenArray ) {
$this->childrenArray = $childrenArray;
}
public function changeValueForKey( $arrKey, $arrValue ) {
foreach ( $this->childrenArray as $key => $value ) {
foreach ( $value as $subKey => $subValue ) {
if ( $arrKey == $subKey ) {
$this->bigArray[ $subKey ] = $arrValue;
$this->childrenArray[ $key ][ $subKey ] = $arrValue;
}
}
}
$this->linkedChildrenArray = (object) $this->childrenArray;
}
}
As you can see, the $arr2 now need to be access from $arrayLink object:
$arr1 = [ 'key1' => 'data1', 'key2' => 'data2' ];
$arr2 = [ 'key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5' ];
$arr3 = [ 'key6' => 'data6' ];
$arrayLink = new ArrayLink( array( 'arr1' => $arr1, 'arr2' => $arr2, 'arr3' => $arr3 ) );
$arrayLink->changeValueForKey( 'key3', 'new value for key 3' );
echo $arrayLink->bigArray['key3']; //new value for key 3
echo $arrayLink->linkedChildrenArray->arr2['key3']; //new value for key 3

Using PHP array iterator to edit array

I have a multidimensional array with variable number of levels of data. That is, I can't be sure how many iterations it will take to reach the Goal level, which is an array. Something like this:
[
'key1' = 'value1',
'key2' = 'value2',
'key3' = [
'key4' => [
'key5' => 'value3'
],
'key6' => 'value4'
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7'
]
],
'key8' => 'value8'],
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10'
]
]
]
I've tried both array_walk_recursive and ArrayIterator, but neither seems to quite get me where I need to be.
I need to go through each element of the array, and if the key is Goal examine the value (eg. the array that Goal holds) and see if that array contains the value Foo.
If Foo is found in the array, I need to add a new value (in addition to Foo-- so call it Bar) to the array and then continue, since there may be more Goals in the parent array.
Is there a way to "stop" the iterator when we find a Goal, without iterating further, and then do the array_search operation?
Edit: Trying somethings along these lines--
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($iterator as $key => $value)
{
if($key == 'Goal')
{
if (is_array($value)) {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
}
}
}
Not entirely sure if this is what you want to achieve but here's a solution which adds Bar to arrays nested in the Goal key:
$array = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => [
'key4' => [
'key5' => 'value3',
],
'key6' => 'value4',
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7',
],
],
'key8' => 'value8',
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10',
],
],
];
function iterate(array $data, $goal = false)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
$value = iterate($value, $key === 'Goal');
} elseif (is_string($value)) {
if (($value === 'Foo') && $goal) {
$data[] = 'Bar';
return $data;
}
}
}
return $data;
}
var_export(iterate($array));
The code generates the following output:
array (
'key1' => 'value1',
'key2' => 'value2',
'key3' =>
array (
'key4' =>
array (
'key5' => 'value3',
),
'key6' => 'value4',
),
'key7' =>
array (
'Goal' =>
array (
0 => 'value5',
1 => 'value6',
2 => 'value7',
),
),
'key8' => 'value8',
'key9' =>
array (
'Goal' =>
array (
0 => 'value9',
1 => 'Foo',
2 => 'value10',
3 => 'Bar',
),
),
)
Iterators in my opinion would be weird to use in these kind of arrays... I would do it with something like this:
/*
Usage:
$wasFound = checkArray( "Goal", "Foo", $the_array);
if ( $wasFound ) echo "Key and Value pair found in the array!";
else { /* not found */ }
*/
function checkArray( $key_to_find, $value_to_find, $my_var, $last_key = NULL ) {
$found = FALSE;
if ( $last_key == $key_to_find && $my_var == $value_to_find )
return TRUE;
if ( $my_var == NULL )
return FALSE;
if ( is_array( $my_var ) ) {
foreach ( $my_var AS $key => $value )
{
if ( $found ) {
/* Do something else if needed when found */
break;
}
$found = checkArray( $key_to_find, $value_to_find, $value, $key );
}
}
return $found;
}
I agree that recursion is the way to do this. The problem with using array_walk_recursive is that you will not be able to see the Goal key, because as per the PHP documentation,
Any key that holds an array will not be passed to the function.
I am not really sure whether using a RecursiveIteratorIterator would be better than just writing a recursive function for it. A function for something like this should be fairly simple.
function addBar(&$array) {
foreach ($array as $key => &$value) {
if ($key === 'Goal') {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
} elseif (is_array($value)) {
addBar($value);
}
}
}
This function takes a reference to your array, so it will update your actual array rather than creating a copy with Bar added to each Goal.

PHP Question: how to array_intersect_assoc() recursively

Let's say I want to do this:
$a = array_intersect_assoc(
array(
'key1' => array(
'key2' => 'value2'
),
'key3' => 'value3',
'key4' => 'value4'
),
array(
'key1' => array(
'key2' => 'some value not in the first parameter'
),
'key3' => 'another value'
)
);
var_dump( $a );
The printed result is:
array
'key1' =>
array
'key2' => string 'value2' (length=6)
It's clear that values associated with 'key2' in both arrays are not the same, however array_intersect_assoc() still return 'key2' => 'value2' as the intersected value.
Is this the expected behavior of array_intersect_assoc()?
Thanks!
Yes, it's the expected behavior, because the comparison is done using string representations, and the function does not recurse down nested arrays. From the manual:
The two values from the key => value pairs are considered equal only if (string) $elem1 === (string) $elem2 . In other words a strict type check is executed so the string representation must be the same.
If you tried to intersect with an array with 'key1' => 'Array', you'd get the same result because the string representation of an array is always 'Array'.
One of the user-contributed notes, by nleippe, contains a recursive implementation that looks promising (I modified the third line to do string comparison on any non-array values):
function array_intersect_assoc_recursive(&$arr1, &$arr2) {
if (!is_array($arr1) || !is_array($arr2)) {
// return $arr1 == $arr2; // Original line
return (string) $arr1 == (string) $arr2;
}
$commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
$ret = array();
foreach ($commonkeys as $key) {
$ret[$key] =& array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);
}
return $ret;
}
function array_key_match_recursive(array $main, array $other, $i = 0, &$result = []) {
foreach($main as $key => $value) {
$k = sprintf('%s%s', str_repeat('=', $i), $key);
if (!isset($other[$key])) {
$result[$k][] = 'not key';
}
if (!is_array($value) && empty($other[$key])) {
$result[$k][] = 'value empty';
}
if (is_array($value) && isset($other[$key])) {
array_key_match_recursive($value, $other[$key], ++$i, $result);
}
}
//return (bool) !$result;
return $result;
}
A function that does what you need:
/**
* Get array intersect assoc recursive.
*
* #param mixed $value1
* #param mixed $value2
*
* #return array|bool
*/
function getArrayIntersectAssocRecursive(&$value1, &$value2)
{
if (!is_array($value1) || !is_array($value1)) {
return $value1 === $value2;
}
$intersectKeys = array_intersect(array_keys($value1), array_keys($value2));
$intersectValues = [];
foreach ($intersectKeys as $key) {
if (getArrayIntersectAssocRecursive($value1[$key], $value2[$key])) {
$intersectValues[$key] = $value1[$key];
}
}
return $intersectValues;
}
My version based on #BoltClock version:
function array_intersect_assoc_recursive($arr1, $arr2) {
if (!is_array($arr1) || !is_array($arr2)) {
return $arr1;
}
$commonkeys = array_keys($arr1);
if (!array_key_exists('$', $arr2)){
$commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
}
$ret = array();
foreach ($commonkeys as $key) {
$ret[$key] = array_intersect_assoc_recursive($arr1[$key], array_key_exists('$', $arr2) ? $arr2['$'] : $arr2[$key]);
}
return $ret;
}
I use this code to filter data inside complex array
example:
$filter = [
'channels' => [
'$' => [
'id' => 1,
'type' => 1,
'count' => 1
]
],
'user' => [
'id' => 1,
'type' => 1
]
];
$data = [
'user' => [
'id' => '1234',
'type' => true,
'counter' => 14,
],
'filteredField' => 4,
'channels' => [
['id' => '567', 'type' => 'other', 'count' => 1345, 'filteredField' => 5,],
['id' => '890', 'type' => 'other', 'count' => 5456, 'filteredField' => 7,],
],
];
print_r(array_intersect_assoc_recursive($data, $filter));
test online:
https://onlinephp.io/c/3be04

Categories