Apply function to every array key - php

I am using Cassandra and I have saved some byte representations as ID. Everything is working fine, however that data (id) is no good for output.
$users = $db->get('1');
echo '<pre>';
print_r($users);
die();
Outputs
Array
(
[��� X��W��c_ ] => Array
(
[id] => ��� X��W��c_
[name] => steve
[surname] => moss
)
[�*B�X��y�~p��~] => Array
(
[id] => �*B�X��y�~p��~
[name] => john
[surname] => doe
)
)
As you can see ID's are some wierd characters, it's because they are byte representations in database. They actually look like \xf5*B\xa0X\x00\x11\xe1\x99y\xbf~p\xbc\xd1~.
In PHPCASSA there is function CassandraUtil::import(); to which I can pass these bytes and it will return guid. It works fine, but I want my array to automatically converted from bytes to guids.
Only option I find is looping through every item in array and assigning new value to it. Somehow I think that it is not the best approach. Is there any other ways to do this?
TL;DR
Have array with bytes like above, need to use CassandraUtil::import(); on array keys and id's to get readable id's. What is the most effective way of doing so.

UPDATE
Sorry, only saw the top level array key, I think you would have to run the function below as well as another one after:
function cassImportWalkRecur(&$item, $key)
{
if ($key == 'id')
$item = CassandraUtil::import();
}
$array = array_walk_recursive($array, 'cassImportWalkRecur');
That should apply it to the ID fields. If you need to check the data first, there maybe a way to detect the encoding, but I am not sure how to do that.
You should be able to create a function and use array_walk to traverse the array and update the keys. Something like:
function cassImportWalk($item, &$key)
{
$key = CassandraUtil::import();
}
$array = array_walk($array, 'cassImportWalk');
Untested (also you may have to change the CassandraUtil usage), but should work.

Unless I am misunderstanding the question this can be done simply and cleanly like so:
$users = $db->get('1');
$keys = array_keys($users);
$readableKeys = array_map("CassandraUtil::import",$keys);
foreach($users as $currentKey => $subArray) {
$readableKey = array_shift($readableKeys);
$subArray['id'] = $readableKey;
$users[$readableKey] = $subArray;
unset($users[$currentKey]);
}

Would array_flip() all keys and values, then array_walk() and apply my function, before doing a final array_flip().

Related

array_walk not changing value

function values($id,$col)
{
$vals = [1=>['name'=>'Lifting Heavy Boxes']];
return $vals[$id][$col];
}
$complete = [1=>["id"=>"2","sid"=>"35","material_completed"=>"1","date"=>"2017-12-18"]];
$form = 'my_form';
array_walk($complete, function(&$d,$k) use($form) {
$k = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
echo 'in walk '.$k."\n";
});
print_r($complete);
the echo outputs:
in walk Lifting Heavy Boxes [12/18/17] (my_form)
the print_r outputs:
Array
(
[1] => Array
(
[id] => 2
[sid] => 35
[material_completed] => 1
[date] => 2017-12-18
)
)
I have another array walk that is very similar that is doing just fine. The only difference I can perceive between them is in the one that's working, the value $d is already a string before it goes through the walk, whereas in the one that's not working, $d is an array that is converted to a string inside the walk (successfully, but ultimately unsuccessfully).
Something I'm missing?
And here's the fixed version:
array_walk($complete, function(&$d,$k) use($form) {
$d = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
});
That's what I was trying to do anyway. I wasn't trying to change the key. I was under the mistaken impression that to change the value you had to set the key to the new value.
You cannot change the key of the array inside the callback of array_walk():
Only the values of the array may potentially be changed; its structure cannot be altered, i.e., the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.
This is also mentioned in the first comment:
It's worth nothing that array_walk can not be used to change keys in the array.
The function may be defined as (&$value, $key) but not (&$value, &$key).
Even though PHP does not complain/warn, it does not modify the key.

Yii2 array of objects, find the one

I have an array in yii2, and ocassionally it's only 1 single object that is not empty (all other element of array is empty) and I don't know which one is it. How can I either find the one that is not empty, or (my idea what I was trying), to create a new array, with array_filter (but I'm not sure if it works also with array of objects), to have only the one object in it.
if (count($ttepk) == 1) {
$ttep_filtered[] = array_filter($ttepk);
$id = $ttep_filtered[0]->id;
}
But it was also not working. I get the error message: PHP Notice – yii\base\ErrorException Trying to get property of non-object.
Before array_filter it looks like this:
Array
(
[3] => app\models\Model Object
(
after array_filter:
Array
(
[0] => Array
(
[3] => app\models\Model Object
(
So it seems, array_filter is not the one I need, or I use it the wrong way.
Can you please help me? Thank you!
You can try something like this
$filtered = array_filter($ttepk, function($item) {
return $item instanceof app\models\Model;
});
if (count($filtered) == 1) {
$id = reset($filtered)->id;
}

Find which keys of separate arrays intersect using a function

Ok so I have two arrays, one is an input array full of data like :
$array1 = ["_token" => "62d46d4h6dfh841df8h", "sku62" => "3e", "name62" => "meh", "sku61" => "3e", "name61" => "mah", "sku64" => "3e", "name64" => "moh"]
The other holds simply id's: $array2 = [64, 74, 61]
edit for clarity: $array1 is a snippet of input from a post request i.e. $array1 = $request->all(); The numbers present within the keys of this array are unique Id's appended on form generation to distinguish between rows with multiple form elements.
Each row has an "update" checkbox also with an appended unique id. When ticked this id shows up in the request e.g. update64.
$array2 was populated by doing a foreach through the request, identifying the update string and isolating the id:
foreach ($array1 as $id => $value) {
$idInt = filter_var($id, FILTER_SANITIZE_NUMBER_INT);
$str = preg_replace('/[0-9]+/', '', $id);
if ($str === "update") {
array_push($array2, $idInt);
}
}
I want a solution that returns the elements from $array1 that have the appended ids found in $array2.
My own attempt looks like this:
$relevant_keys = function($key1, $key2) {
return ((preg_replace('/[0-9]+/', '', $key1) === $key2)) ? 1 : -1;
};
$filtered = array_intersect_ukey($array1, array_flip($array2), $relevant_keys);
However $filtered is returning empty and if I dd($key2) within the function it's not even returning an element from $array2, I get something from $array1 instead so this has left me confused.
Would appreciate any help.
Here's the solution to the exact problem you posted:
$filtered = [];
foreach ($array1 as $key => $value)
{
if ( ! preg_match('/(\d+)$/', $key, $matches)) continue;
if ( ! isset($matches[1]) || ! in_array($matches[1], $array2)) continue;
$filtered[$key] = $value;
}
But I'm not sure you're approaching this correctly. That input looks suspicious.
Are you sure there's no better way to format the request?
I have a few important insights to share based on your coding attempt.
array_intersect_ukey() should be the perfect function call for his task, but alas, it is not. I'll tell you why.
array_intersect_ukey() suffers in the same way as array_intersect_uassoc() and array_uintersect_uassoc() because the internal algorithm will stop looking for additional qualifying keys after it encounters its first one. I first came upon this reality here.
Also, the way that you've declared and used the custom function arguments ($key1 and $key2) indicates that you believe $key1 always relates to the first nominated array and $key2 always relates to the second nominated array. This is not true and I have seen many developers with this same false impression. The truth is that under the hood, the two parameters fed into the custom function may come from either array.
For the reasons in #1, I'll recommend that you shift your focus to array_filter(). By establishing a lookup array containing whitelisted keys and filtering on keys, you can swiftly filter your data. Inside the callback, I am using trim() to remove the letters before the id number at the end. This is just one way of isolating the whole number at the end of each key.
Code: (Demo)
$lookup = array_flip($array2);
var_export(
array_filter(
$array1,
fn($key) => isset($lookup[ltrim($key, 'a..z')]),
ARRAY_FILTER_USE_KEY
)
);
Output:
array (
'sku61' => '3e',
'name61' => 'mah',
'sku64' => '3e',
'name64' => 'moh',
)

PHP RecursiveIteratorIterator overwrites array keys

Here is the function I wrote to flatten the multidimensional PHP array:
function flattenArray(array $array) {
if (! is_array($array)) {
throw new Exception ("Please specify an array.");
}
$resultArray = [];
$arrayObject = new RecursiveArrayIterator($array);
foreach(new RecursiveIteratorIterator($arrayObject) as $key => $value) {
$resultArray[$key] = $value;
}
return $resultArray;
}
And using it:
$arr = [
["sitepoint", "phpmaster"],
["buildmobile", "rubysource"],
["designfestival", "cloudspring"],
"not an array"
];
print_r(flattenArray($arr));
Result:
Array
(
[0] => designfestival
[1] => cloudspring
[3] => not an array
)
However, I was expecting:
0: sitepoint
1: phpmaster
2: buildmobile
3: rubysource
4: designfestival
5: cloudspring
6: not an array
But it is re-generating indexes as in:
0: sitepoint
1: phpmaster
0: buildmobile
1: rubysource
0: designfestival
1: cloudspring
3: not an array
So how do I modify function to get all elements of the array not just three:
Array
(
[0] => designfestival
[1] => cloudspring
[3] => not an array
)
Thanks for the help
if (!is_array($array)) is superfluous, since you have the array type hint in the function signature and PHP will enforce that.
You are overwriting the keys. Those elements all have the same keys in their respective subarray. Since it's not an associative array, you don't need to preserve the keys. Instead of
$resultArray[$key] = $value;
just do
$resultArray[] = $value;
I too hit this limitation with RecursiveIteratorIterator.
At first I had been using this concise, one-line array flattener wherever needed:
$outputs = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator([$inputs])), FALSE);
similar to your longer function above.
All was great: I was able to "normalize" my data structure into a 1D array, no matter if the incoming $inputs parameter came into my Symfony2 Controller as a single String/float value, 1D or 2+D multidimensional array. (I was writing a callback from AJAX that is to respond with JSON-formatted tables for an interactive Highcharts.com chart to be able to render, in my financial app.)
However, it refused to draw because in the final step, each data cell was in the form
0 => float 100.662
even though I had taken care that my $inputs creature only contained cells in the form:
'2002-04-30' => float 100.662
So basically the above array-flattening line had killed the keys (DateStamp).
Fed up with studying RecursiveIteratorIterator, I just broke down and came up with my own array_flatten that preserves keys, if any:
static public function array_flatten($inObj)
{
$outObj = []; $inObj=[$inObj];
array_walk_recursive($inObj, function ($incell, $inkey) use (&$outObj)
{
$outObj[$inkey] = $incell;
} );
return $outObj;
}
Note that you are responsible for ensuring that the keys in $inObj are globally unique (and either string or int type), otherwise, I don't know how my function behaves. Probably overwrites the value using the same key name?

In PHP, how can I access a child object when I don't know it's name?

Using json_decode, I've ended up with an object that looks like this:
$data->foo->bar->1234567->id
I want to access id. There are two problems, both with the number 1234567:
It's an illegal property name.
The number will differ each time, and I can't predict what the number will be. I need a way of accessing id, even when I don't know the number.
I know I can overcome problem (1) with curly braces, but I don't know how to overcome (2). I don't want to use get_object_vars, because the object is likely to be very large, and that function is very slow.
My current solution is simply
foreach ($data->foo->bar as $id); but that feels rather hacky. Is there a better way?
From my comment above, using json_decode(,true) and then resetting.
The example json array looks like:
Array (
[foo] => Array (
[bar] => Array (
[1234567] => Array (
[id] => 1234
)
)
)
)
The code:
<?php
$data = json_decode('{"foo":{"bar":{"1234567":{"id":1234}}}}', true);
reset($data['foo']['bar']);
$number = key($data['foo']['bar']);
echo $data['foo']['bar'][$number]['id'];
Output: 1234
In case you don't need the whole array anymore and only want to get the id you can get it like this:
<?php
$data = json_decode('{"foo":{"bar":{"1234567":{"id":1234}}}}', true);
echo array_shift($data['foo']['bar'])['id'];
Only works if the unknown key is the first element of bar. array_shift removes the element from $data.

Categories