Converting string containing multidimensional array keys and values PHP - php

I use TPP API for check domain availability and domain register but i receive response in string.
Get Session, return stringOK: t73484678463765
Domain check, return string woohoo123.nz: OK: Minimum=1&Maximum=2
In other case, return string woohoo123.nz: ERR: 102, This is message
When It return OK it has & in child but when ERR that time it has ,
I want convert return string into array
such as input woohoo123.nz: OK: Minimum=1&Maximum=2 and output following array
[
'woohoo123.nz' => [
'OK' => [
'Minimum' => 1,
'Maximum' => 2,
]
]
]
input woohoo123.nz: ERR: 102, This is message and output following array
[
'woohoo123.nz' => [
'ERR' => [
'code' => 102,
'message' => 'This is message',
]
]
]
I like more to reuse code, I prefer recursive and callback but not sure in this case.

Not 100% sure if this is what you are looking for. It works for your examples, but will only continue to work if the input strings follow that format strictly.
function stringToArray($inputStr) {
$array = [];
$topComponents = explode(': ',$inputStr);
$parametersStr = $topComponents[count($topComponents) -1];
if (strpos($parametersStr,'&') !== FALSE) {
$tmpArr = explode('&',$parametersStr);
foreach ($tmpArr as $val) {
$comp = explode('=',$val);
$array[$comp[0]] = $comp[1];
}
} else if ($topComponents[count($topComponents) - 2] === "ERR") {
$tmpArray = explode('ERR: ',$parametersStr);
$tmpArray = explode(', ',$tmpArray[0]);
$array = [
"code" => intval($tmpArray[0]),
"message" => $tmpArray[1]
];
} else {
$array = $parametersStr;
}
for ($i=count($topComponents) -2; $i >= 0; $i--) {
$newArray = [];
$newArray[$topComponents[$i]] = $array;
$array = $newArray;
}
return $array;
}
print_r(stringToArray("OK: t73484678463765"));

Related

refactor multidimensional json in php by keys

I'm working on project and I'm trying to refactor a json object By array of $keys
for example:
as input:
{
"message": "User Detail",
"code": 200,
"error": false,
"results": {
"user": {
"id": 2,
"name": "ali",
"country": {
"id": 50,
"name": "EGY"
}
},
"access_token": "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o"
}
}
as input: array of $keys
$kyes = [
'code',
'user'=>[
'id',
'country'=>['name']
]
]
expect to return:
{
"code": 200,
"user": {
"id": 2,
"country": {
"name": "egy",
}
}
}
code I have tried:
public function filter_multidimensional(array $array, array $keys){
$val = [];
foreach ($array as $key => $value) {
if (in_array($key,$keys)){
$val[$key] = $value;
}elseif (is_array($value)) {
$val[$key] = $this->filter_multidimensional($keys,$value);
}
}
return $val;
}
//-------
$this->filter_multidimensional($json,['code','user'=>['id','country'=>['name']]])
Unfortunately output:
update 1
the json input is not const, so my code must be adapt. and that's I'm trying to do.
Thanks for #404-not-found his code was amazing but missing a small thing
which is in this code
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional($array, $key);
}
you still give the find function the base array which will return the first value it will find in the case of ['user'=>['id',"country"=>['id']]] and the solution for this will be passing the parent array of the object key
So the full code will be
<?php
function filter_multidimensional(array $array, array $keys) {
if (empty($keys) || empty($array)) {
return [];
}
$val = [];
// In the structure of $keys, both key and value are technically "keys".
// In the example `['code','user'=>['id']]`, 'code' and 'id' are both array *values*,
// while 'user' is a key.
//
// So, $objectKey is a search key which contains sub-keys, while $key is just a regular string.
foreach ($keys as $objectKey => $key) {
// If $key is an array, then recursively search, and save the results to the $objectKey string.
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional(findTill($array,$objectKey), $key);
}
// If the desired key exists, then save the value.
else if (array_key_exists($key, $array)) {
$val[$key] = $array[$key];
}
// Otherwise, $key is a string, but it does not exist at this level of $array,
// so search the next-level up in $array, and merge the results into this level of $val.
else {
$val = array_merge($val, filter_multidimensional(nextLevel($array), [$key]));
}
}
return $val;
}
function findTill($array,$key) {
if (array_key_exists($key, $array)) {
return $array[$key];
}
return findTill(nextLevel($array),$key);
}
/**
* Create an array that contains only the array values from $array.
*
* Eg: If given ['a' => '1', 'b' => ['foo' => '2'], 'c' => ['hello' => 'world']],
* then return ['foo' => '2', 'hello' => 'world']
*
* #param array $array
* #return array
*/
function nextLevel(array $array) {
// Merge all of the array values together into one array
return array_reduce(
// Strip the keys
array_values(
// Filter to return only keys where the value is an array
array_filter(
$array,
function ($value) {
return is_array($value);
}
)
),
'array_merge',
[]
);
}
//-------
$obj = [
"message" => "User Detail",
"code" => 200,
"error" => false,
"results" => [
"user" => [
"id" => 2,
"name" => "ali",
"country" => [
"id" => 50,
"name" => "EGY",
],
],
"access_token" => "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o",
],
];
$result = filter_multidimensional($obj,['code','user'=>['id','country'=>['id','name']],"access_token"]);
I believe this method works. I flipped your logic, and instead of searching the $array to see if it's keys match any of those in $keys, I instead recursively searched $keys to see if $array had any matching values.
function filter_multidimensional(array $array, array $keys) {
if (empty($keys) || empty($array)) {
return [];
}
$val = [];
// In the structure of $keys, both key and value are technically "keys".
// In the example `['code','user'=>['id']]`, 'code' and 'id' are both array *values*,
// while 'user' is a key.
//
// So, $objectKey is a search key which contains sub-keys, while $key is just a regular string.
foreach ($keys as $objectKey => $key) {
// If $key is an array, then recursively search, and save the results to the $objectKey string.
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional($array, $key);
}
// If the desired key exists, then save the value.
else if (array_key_exists($key, $array)) {
$val[$key] = $array[$key];
}
// Otherwise, $key is a string, but it does not exist at this level of $array,
// so search the next-level up in $array, and merge the results into this level of $val.
else {
$val = array_merge($val, filter_multidimensional(nextLevel($array), [$key]));
}
}
return $val;
}
/**
* Create an array that contains only the array values from $array.
*
* Eg: If given ['a' => '1', 'b' => ['foo' => '2'], 'c' => ['hello' => 'world']],
* then return ['foo' => '2', 'hello' => 'world']
*
* #param array $array
* #return array
*/
function nextLevel(array $array) {
// Merge all of the array values together into one array
return array_reduce(
// Strip the keys
array_values(
// Filter to return only keys where the value is an array
array_filter(
$array,
function ($value) {
return is_array($value);
}
)
),
'array_merge',
[]
);
}
//-------
$result = filter_multidimensional($obj,['code','user'=>['id','country'=>['name']]]);
You could use a combination of data_get and data_set to get what you want.
Change the input a bit so it's more consistent. Dot notation would be easiest.
$array = [
"message" => "User Detail",
"code" => 200,
"error" => false,
"results" => [
"user" => [
"id" => 2,
"name" => "ali",
"country" => [
"id" => 50,
"name" => "EGY",
],
],
"access_token" => "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o",
],
];
$keys = [
'code' => 'code',
'user.id' => 'results.user.id',
'user.country.name' => 'results.user.country.name',
];
$results = [];
foreach ($keys as $set_position => $get_position) {
data_set($results, $set_position, data_get($array, $get_position));
}

PHP Recursively calculate a formula in an array in reverse

I would like te perform a calculation on the following array
$array = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
"90%"
]
],
0.95
]
]
];
eventually the calculation would traverse into:
1. add(ceil(multiply(9.95, 90%)), 0.95)
2. ceil(multiply(9.95, 90%)) + 0.95
3. ceil(8.955) + 0.95
4. 9 + 0.95
5. 9.95
I've tried recursively looping through the array using array_walk_recursive and custom functions which basically to the same.
but the problem is that the add cannot calculate the ceil before it has calculated the multiply.
So how can i recusively in reversed order calculate all the parts of the array?
I'm slowly loosing my mind over this and starting to question if it's even possible.
All ideas are greatly appreciated
Another option, that verify the indexes of the array are valid functions and allows add and multiple with more than 2 values.
function multiply(...$a)
{
return array_product($a);
}
function add(...$a)
{
return array_sum($a);
}
function applyCalculation(array $data)
{
$firstKey = array_keys($data)[0];
$arguments = [];
foreach ($data[$firstKey] as $name => $value) {
// if argument is array with existing function, we pass it recursively
if (function_exists($name) && is_array($value)) {
$result = applyCalculation([$name => $value]);
$result = is_array($result) ? $result : [$result];
$arguments = array_merge($arguments, $result);
} elseif (!is_array($value)) {
// if not array, just append to arguments
$value = strpos($value, '%') !== false ? str_replace('%','',$value) / 100 : $value;
$arguments[] = $value;
} elseif (is_array($value)) {
// error here, the index is not a valid function
}
}
return $firstKey(...$arguments);
}
echo applyCalculation($array['calculation']);
So it will work for your example:
$data = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
'90%',
]
],
0.95
]
]
];
// will give you 9.95
But it also works for more complex cases::
$array = [
"calculation" => [
"add" => [
"ceil" => [
"add" => [
"multiply" => [
9.95,
'90%'
],
"add" => [
1,
3
],
]
],
0.95,
3
]
]
];
// will give you 16.95
Assuming that you have this structure, you may use this code. If you need more than 2 values to be "added" or "multiplied" you may need to use some splat operator or change the logic a little bit:
<?php
$array = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
"90%"
]
],
0.95
]
]
];
function add($a, $b) {
return $a + $b;
}
function multiply($a, $b) {
return $a * $b;
}
function calculation($arr, $operation = null) {
$elements = [];
foreach($arr as $k => $value) {
if (is_numeric($k)) {
// change % with valid numeric value
if(strpos($value, '%')) {
$value = str_replace('%','',$value) / 100;
}
//if there are no operations, append to array to evaluate under the operation
$elements[] = $value;
} else {
//if there are operations, call the function recursively
$elements[] = calculation($value, $k);
}
}
if ($operation !== null) {
return call_user_func_array($operation, $elements);
}
return $elements[0];
}
echo calculation($array['calculation']);

Find elements with empty value in multidimensional array

I have an array like the below:
$arrays = [
'a' => [
'name' => "Name 1",
'age' => "99",
'add' => ""
],
'b' => [
'name' => "Name 2",
'age' => "99",
'add' => "Add2"
],
'c' => [
'name' => "Name 3",
'age' => "99",
'add' => "Add3"
],
'd' => [
'name' => "",
'age' => "",
'add' => "Add4"
]
];
I want to get a result like:
$res = [
'a' => ['add'],
'd' => ['name','age']
];
I have tried with the below code, but it returns 1.
$status = array_walk_recursive($arrays, function($v, $k) {
global $output;
if (empty($v) && $v !== 0)
$output[$k] = $v;
});
I want to do it without using any loops because my real input array is very large and I am concerned with performance.
If your input is always of a fixed depth, you can map the existing values to the keys of all empty items:
$output = array_map(function($row) {
return array_keys(array_filter($row, function ($e) {
return empty($e) && $e !== 0;
}));
}, $arrays);
The outer function runs for each "row", the value of which is then converted into a list of all keys with empty values (excluding zeroes, as in the question).
This will keep the outer keys B & C as empty arrays, so if you want them to be removed as well then run another iteration of array_filter over the result:
$output = array_filter($output)
See https://3v4l.org/c23ZB
As mentioned in the comments, there are still several loops going on here, they're just not as visible in the code. A regular foreach loop might end up being a lot easier to read, and possibly perform faster as well.
You can use next combination of array_walk & array_filter:
$result = [];
array_walk(
$arrays,
function($el, $key) use(&$result) {
$empty = array_filter($el, function($el){return $el == "";});
$empty_keys = array_keys($empty);
if (count($empty_keys)) $result[$key] = $empty_keys;
}
);
Try it here
This is another way to achieve your desired output.
$result = [];
foreach($arrays as $key => $value) {
$empty_arr = array_filter($value, function ($ele) {
return empty($ele);
});
$empty_arr_keys = array_keys($empty_arr);
if(!empty($empty_arr_keys)) $result[$key] = $empty_arr_keys;
}
print_r($result);
#iainn's answer can be sharpened up by calling !strlen() on the deep values.
Code: (Demo)
var_export(
array_filter(
array_map(
fn($row) => array_keys(
array_filter(
$row,
fn($v) => !strlen($v)
)
),
$array
)
)
);
But you will end up making fewer iterations and writing cleaner, more intuitive/readable code if you use classic loops. This is how I would write it in my own project:
Code: (Demo)
$result = [];
foreach ($array as $rowKey => $row) {
foreach ($row as $key => $value) {
if (!strlen($value)) {
$result[$rowKey][] = $key;
}
}
}
var_export($result);

PHP, array - mixed order, steam api returns different order

how I can sort this by number, 0 - 1+ not by first letter?
error_reporting(E_ALL);
ini_set("display_errors", 1);
$players = array();
$playerIds = [
'76561197972192473',
'76561198972352439',
'76561198087304080',
'76561198799985528',
'76561198338485290'
];
$url = "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=294CFDFSFCWE5EG5FE4&steamids=".implode(',', $playerIds);
$json_object= file_get_contents($url);
$json_decoded = json_decode($json_object);
foreach ($json_decoded->response->players as $key=>$player) {
$players[] = [
'name' => $player->personaname,
'profileurl' => $player->profileurl,
'avatarfull' => $player->avatarfull,
'personastate' => $player->personastate
];
}
usort($players, function($a, $b) {
if ($a['name'] === $b['name']) {
return 0;
}
return ($a['name'] < $b['name']) ? -1 : 1;
});
When I remove usort, steam api will return ids everytime in different order, so can you help me how I can make it starts with 0 from playerIds.
Just add the steamid as the index and then sort it on keys:
foreach ($json_decoded->response->players as $key=>$player) {
$players[$player->steamid] = [
'name' => $player->personaname,
'profileurl' => $player->profileurl,
'avatarfull' => $player->avatarfull,
'personastate' => $player->personastate
];
}
ksort($players);
Alternately, you could do it the way you're doing it but add the steamid to the $players array:
$players[] = [
'id' => $player->steamid,
'name' => $player->personaname,
'profileurl' => $player->profileurl,
'avatarfull' => $player->avatarfull,
'personastate' => $player->personastate
];
Then sort on the id:
array_multisort(array_column($players, 'id'), SORT_DESC, $players);
There is maybe a parameter to be passed to the API that will return them sorted the way you want, not sure.

Get key value from multidimensional array not working

I am trying to get a value from a multidimensional array by its name 'code'. When I dump and die the first way it returns the correct value. When I then want to use that in the code, it gives the error "Undefined index: code". I also used the array_column way, that dd an empty array.
The code that should get the correct $code:
foreach ($houses as $house) {
$code = $house['code']; //Returns correct value in a dd, not in the code
$code = array_column($house, 'code'); //Returns empty array in dd, later gives the error Array to string conversion in the file_get_contents
$a = file_get_contents('some-url' . $code . '/surveys');
$a = json_decode($a, true);
$surveys = $a['surveys'];
$completedSurveys = $a['surveysCompleted'];
$done = 0;
$ndone = 0;
foreach ($completedSurveys as $complete) {
if($complete) {
$done++;
} else if(!$complete) {
$ndone++;
} else {continue;}
}
}
$house dump:
array:30 [
id: ''
project: integer
city: ''
streetName: ''
houseNumber: ''
code: ''
fullStreet: ''
forms: array:1 [
0: integer
]
]
$code dump
$house['code']: "AB12-CD34-EF56-GH78"
array_column($house, 'code'): []
I would like to know the solution to this so that I can use the $code in the url to get the correct things back from the api.
You can use array_column on your entire array, not the sub-arrays.
$houses =
[
[
'code' => '23',
'city' => 'Dublin'
],
[
'city' => 'Canberra'
],
[
'code' => '47',
'city' => 'Amsterdam'
]
];
var_export(array_column($houses, 'code'));
Output:
array (
0 => '23',
1 => '47',
)
You could then do something along these lines.
$get_survey = function ($code) {
if($response = file_get_contents("http://example.com/$code/surveys"))
if($json = json_decode($response, true))
return $json;
};
$completed = [];
foreach(array_column($houses, 'code') as $code) {
if($survey = $get_survey($code)) {
// survey count calculation.
} else {
$completed[$code] = null; // failure to get survey data.
}
}

Categories