Having this array:
Array
(
[_block1] => Array
(
[list] => Array
(
[sub-list] => Array
(
)
)
[links] => Number
[total] => Number
...
)
[_block2] => Array
(
[#attributes] => Array
(
)
[title] => ...
[data] => Array ()
...
)
[_block3] => Array
(
..
)
)
Those blocks contain data returned by api. Knowing that each api returns data in a different way/structure I need to measure/calculate the data/size inside of each and one of them and then do if data > X or < do something.
Is it possible? I have searched google but I only found count() and that isn't what I need to make this work.
Edit:
Each and of the those blocks contain many other sub blocks, and I was thinking of calculating the data size in bytes, because count wont do the job here.
echo mb_strlen(serialize((array)$arr), '8bit');
If I understood well your question, you need the size of each "block" subarray inside the main array.
You can do something like this:
$sizes = array();
foreach($returnedArray as $key => $content) {
$sizes[$key] = count($content);
}
The $sizes array will be an associative array which the various "block"s as keys and the size of the data as values.
Edit:
after the edit of the question, if the data inside the innermost arrays are strings or integers you can use a function like this:
function getSize($arr) {
$tot = 0;
foreach($arr as $a) {
if (is_array($a)) {
$tot += getSize($a);
}
if (is_string($a)) {
$tot += strlen($a);
}
if (is_int($a)) {
$tot += PHP_INT_SIZE;
}
}
return $tot;
}
assuming to have only ASCII-encoded strings.
To get the size in bytes you can use the below code.
$serialized = serialize($foo);
if (function_exists('mb_strlen')) {
$size = mb_strlen($serialized, '8bit');
} else {
$size = strlen($serialized);
}
I hope it will be helpful.
Do you mean something like this?
$x = 32;
foreach($blocks as $key => $block)
{
if(getArraySize($block) < $x)
{
//Do Something
}else
{
//Do another thing
}
}
//Recursive function
function getArraySize($array)
{
$size = 0;
foreach($array as $element)
{
if(is_array($element))
$size += getArraySize($element);
else
$size += strlen($element);
}
return $size;
}
Related
I have array like this
$arr=[["a","b"],["b","c"],["d","e"],["f","c"]];
if sub arrays share same value they should be be merged to one array
expected output:
$arr=[["a","b","c","f"],["d","e"]];
I`m trying to avoid doing foreach inside foreach for solving this.
It seems your inner arrays always have 2 items. so nested loops aren't necessary. Here is a solution which I originally wrote in JS but it should work just as good and most efficient in PHP:
$arr=[["a","b"],["b","c"],["d","e"],["f","c"],["h","e"]];
$output = [];
$outputKeys = [];
$counter = 0;
foreach($arr as $V) {
if(!isset($outputKeys[$V[0]]) && !isset($outputKeys[$V[1]])) {
$output[$counter] = [$V[0], $V[1]];
$outputKeys[$V[0]] = &$output[$counter];
$outputKeys[$V[1]] = &$output[$counter];
$counter++;
}
elseif(isset($outputKeys[$V[0]]) && !isset($outputKeys[$V[1]])) {
array_push($outputKeys[$V[0]], $V[1]);
$outputKeys[$V[1]] = &$outputKeys[$V[0]];
}
elseif(!isset($outputKeys[$V[0]]) && isset($outputKeys[$V[1]])) {
array_push($outputKeys[$V[1]], $V[0]);
$outputKeys[$V[0]] = &$outputKeys[$V[1]];
}
}
var_dump($output); // [["a","b","c","f"],["d","e","h"]]
DEMO (click the execute button)
Pointers are your friends. Use them :)
The following algorithm should do what you want. It simply checks through each item and checks if it already exists in the newly created array, and if it does it adds it to that item instead of a new one:
<?php
$arr=[["a","b"],["b","c"],["d","e"],["f","c"]];
$newArr = [];
foreach ($arr as $items) {
$newKey = null;
foreach ($items as $item) {
foreach ($newArr as $newItemsKey => $newItems) {
if (in_array($item, $newItems)) {
$newKey = $newItemsKey;
break 2;
}
}
}
if ($newKey !== null) {
$newArr[$newKey] = array_merge($newArr[$newKey], $items);
} else {
$newArr[] = $items;
}
}
$newArr = array_map('array_unique', $newArr);
print_r($newArr);
Output:
Array
(
[0] => Array
(
[0] => a
[1] => b
[3] => c
[4] => f
)
[1] => Array
(
[0] => d
[1] => e
)
)
DEMO
This is solution I get for now.
$arr=[["a","b","c","f"],["d","e"]];
$sortedArray = sortFunction($arr,0,array());
function sortFunction($old,$index,$new) {
if ($index == sizeof($old)) return $new;
for ($i = 0; $i<sizeof($new); $i++) {
if (count(array_intersect($new[$i],$old[$index]))) {
$new[$i] = array_unique(array_merge($old[$index],$new[$i]), SORT_REGULAR);
return sortFunction($old,$index + 1,$new);
}
}
$new[] = $old[$index];
return sortFunction($old,$index + 1,$new);
}
Up until now, I've been using the snippet below to convert an XML tree to an array:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
..however, I'm now working with an XML that has duplicate key values, so the array is breaking when it loops through the XML. For example:
<users>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
Is there a better method to do this that allows for duplicate Keys, or perhaps a way to add an incremented value to each key when it spits out the array, like this:
$array = array(
users => array(
user_1 => x,
user_2 => y,
user_3 => z
)
)
I'm stumped, so any help would be very appreciated.
Here is a complete universal recursive solution.
This class will parse any XML under any structure, with or without tags, from the simplest to the most complex ones.
It retains all proper values and convert them (bool, txt or int), generates adequate array keys for all elements groups including tags, keep duplicates elements etc etc...
Please forgive the statics, it s part of a large XML tools set I used, before rewriting them all for HHVM or pthreads, I havent got time to properly construct this one, but it will work like a charm for straightforward PHP.
For tags, the declared value is '#attr' in this case but can be whatever your needs are.
$xml = "<body>
<users id='group 1'>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
<users id='group 2'>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
</body>";
$result = xml_utils::xml_to_array($xml);
result:
Array ( [users] => Array ( [0] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [#attr] => Array ( [id] => group 1 ) ) [1] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [#attr] => Array ( [id] => group 2 ) ) ) )
Class:
class xml_utils {
/*object to array mapper */
public static function objectToArray($object) {
if (!is_object($object) && !is_array($object)) {
return $object;
}
if (is_object($object)) {
$object = get_object_vars($object);
}
return array_map('objectToArray', $object);
}
/* xml DOM loader*/
public static function xml_to_array($xmlstr) {
$doc = new DOMDocument();
$doc->loadXML($xmlstr);
return xml_utils::dom_to_array($doc->documentElement);
}
/* recursive XMl to array parser */
public static function dom_to_array($node) {
$output = array();
switch ($node->nodeType) {
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
$output = trim($node->textContent);
break;
case XML_ELEMENT_NODE:
for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
$child = $node->childNodes->item($i);
$v = xml_utils::dom_to_array($child);
if (isset($child->tagName)) {
$t = xml_utils::ConvertTypes($child->tagName);
if (!isset($output[$t])) {
$output[$t] = array();
}
$output[$t][] = $v;
} elseif ($v) {
$output = (string) $v;
}
}
if (is_array($output)) {
if ($node->attributes->length) {
$a = array();
foreach ($node->attributes as $attrName => $attrNode) {
$a[$attrName] = xml_utils::ConvertTypes($attrNode->value);
}
$output['#attr'] = $a;
}
foreach ($output as $t => $v) {
if (is_array($v) && count($v) == 1 && $t != '#attr') {
$output[$t] = $v[0];
}
}
}
break;
}
return $output;
}
/* elements converter */
public static function ConvertTypes($org) {
if (is_numeric($org)) {
$val = floatval($org);
} else {
if ($org === 'true') {
$val = true;
} else if ($org === 'false') {
$val = false;
} else {
if ($org === '') {
$val = null;
} else {
$val = $org;
}
}
}
return $val;
}
}
You can loop through each key in your result and if the value is an array (as it is for user that has 3 elements in your example) then you can add each individual value in that array to the parent array and unset the value:
foreach($a as $user_key => $user_values) {
if(!is_array($user_values))
continue; //not an array nothing to do
unset($a[$user_key]); //it's an array so remove it from parent array
$i = 1; //counter for new key
//add each value to the parent array with numbered keys
foreach($user_values as $user_value) {
$new_key = $user_key . '_' . $i++; //create new key i.e 'user_1'
$a[$new_key] = $user_value; //add it to the parent array
}
}
var_dump($a);
First of all this line of code contains a superfluous cast to array:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
^^^^^^^
When you JSON-encode a SimpleXMLElement (which is returned by simplexml_load_string when the parameter could be parsed as XML) this already behaves as-if there would have been an array cast. So it's better to remove it:
$sxml = simplexml_load_string($xml);
$array = json_decode(json_encode($sxml), 1);
Even the result is still the same, this now allows you to create a subtype of SimpleXMLElement implementing the JsonSerialize interface changing the array creation to your needs.
The overall method (as well as the default behaviour) is outlined in a blog-series of mine, on Stackoverflow I have left some more examples already as well:
PHP convert XML to JSON group when there is one child (Jun 2013)
Resolve namespaces with SimpleXML regardless of structure or namespace (Oct 2014)
XML to JSON conversion in PHP SimpleXML (Dec 2014)
Your case I think is similar to what has been asked in the first of those three links.
Ok I'm seriously stuck, I've been working on a nav menu that I just cannot get to function how I want it to so i've changed tack but am now stuck again so any help would be much appreciated and desperately needed!
I have the code below and am trying to do the following - I have an array that holds info for all the pages of a site and then another array that holds the ids of the pages that are child pages. What I want to do is use a foreach loop to loop through all the pages of the first array and check whether their ids are in the array of child ids or not. If they are not then they are top level nav pages and I want to output some code and then set up another foreach loop which will check whether any subpages have a parent id of the current page and so on.
I can't seem to work out how to compare $b with the ids in the $childpages array no matter what I try! Is this even possible and if so how please?
This is the first section of what im trying at present
<?php function buildMenu4 ($allpages, $childpages) {
foreach ($allpages as $pages){
$a = $pages['parentid'];
$b = $pages['id'];
$c = $childpages;
echo "<ul>\n";
if (!in_array($b, $c)) {
DO SOMETHING..........
Array contents of $c:
Array
(
[0] => Array ( [id] => 6 )
[1] => Array ( [id] => 15 )
[2] => Array ( [id] => 100 )
[3] => Array ( [id] => 101 )
[4] => Array ( [id] => 103 )
[5] => Array ( [id] => 104 )
[6] => Array ( [id] => 105 )
)
edit ---------------------------------
I have reworked my code and am back to a variation of where I was a couple of days ago!! Anyway the code below works as intended until I try to loop it and then it just echoes the results of the first foreach loop e.g. foreach ($allpages as $pages){..... but fails to do anything else.
I am trying to make a function called loopMenu and then run this recursively until there are no more pages to be displayed. I have tried to write the function as shown with the pusedo code in the code block below but I just can't get it to work. I may have muddled up some of the arguments or parameters or perhaps I have just made a big mistake somewhere but I can't see it - any help would be hugely appreciated and desperately needed!
<?php function buildMenu6 ($allpages, $childpageids, $childpages, $subchildpages) {
foreach ($childpageids as $childid){
$c[] = $childid['id'];
};
echo "<ul>\n";
foreach ($allpages as $pages){
$a = $pages['parentid'];
$b = $pages['id'];
if (!in_array($b, $c)){
echo "<li>" . $pages['linklabel'] . "";
WHERE I WANT THE FUNCTION TO START E.G. function loopMenu($childpages, $subchildpages){...the code that follows....
echo"<ul>\n";
foreach ($childpages as $childparent) {
$d = $childparent['parentid'];
$e = $childparent['id'];
if (($d == $b) or ($d == $g)) {
echo "<li>" . $childparent['linklabel'] . "";
echo "<ul>\n";
foreach ($subchildpages as $subchild){
$g = $subchild['id'];
$f = $subchild['parentid'];
if ($f == $e){
echo "<li>" . $subchild['linklabel'] . "";
WHERE I TRY TO RERUN THE FUNCTION USING loopMenu($childparent, $subchild);
echo "<li/>";
};
};
echo"</ul>\n";
echo "</li>";
};
};
echo "</ul>\n";
WHERE I WANT MY FUNCTION TO END E.G. };
echo "</li>";
};
};
echo "</ul>\n";
}; ?>
Then I call the main buildMenu6 function like so:
<?php buildMenu6($pageids, $childPageIds, $childPageArray, $childPageArray); ?>
You need a nested foreach (I've changed your var names or used the original ones for readability):
foreach($allpages as $page) {
foreach($childpages as $child) {
if($page['id'] == $child['id']) {
//do something
break;
}
}
}
Or PHP >= 5.5.0 use array_column:
$childids = array_column($childpages, 'id');
foreach($allpages as $page) {
if(in_array($page['id'], $childids)) {
//do something
}
}
As a kind of hybrid:
foreach($childpages as $child) {
$childids[] = $child['id'];
}
foreach($allpages as $page) {
if(in_array($page['id'], $childids)) {
//do something
}
}
foreach ($allpages as $pages){
$a = $pages['parentid'];
$b = $pages['id'];
$c = $childpages;
echo "<ul>\n";
// In array isn't aware of the 'id' keys
$found = false;
foreach ($c as $id => $value) {
if ($value == $b) {
$found = true;
}
}
if ($found) {
// DO SOMETHING
}
according to THIS SO ANSWER, array_key_exists is (marginally) the fastest array lookup for php.
since, from your description, it seems reasonable to suppose that [id] is a PRIMARY key and, therefore, unique, i would change the [id] dimension and put values directly in its lieu:
$c[105] = NULL; // or include some value, link page URL
... // some code
$needle = 105;
... // some more code
if (array_key_exists($needle,$c)) {
instead of
$c[5] = 105; // $c is the haystack
... // some code
$needle = 105;
... // some more code
foreach ($c as $tempValue) {
if ($tempValue == $needle) {
in case you want to put values in $c, then you could also use isset (it will return FALSE if array value is NULL for said key).
I am developing a user driven eCommerce website and need some help. What I have is a function that will loop through an array remove duplicates and how many times they occur. I then need to run a function on each of those extracted duplicates as many times as they occur. The code I have so far works, but breaks when there are multiple duplicates with the same repetition count. Here is the code I have made so far..
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14); //breaks the code
$affiliates = array(11,11,13,13,13,14,14,14,14,12,12,12,12,12); // works fine
$array = array();
$match_count = array();
foreach($affiliates as $key => $affiliate) {
$array[] = $affiliate;
}
arsort($array); // keeps array index in order
foreach($array as $arrays) {
if(array_value_count($arrays,$array) > 1) {
$match_count[] = array_value_count($arrays,$array);
}
}
$match_count = array_unique($match_count);
$array_unique = arrayDuplicate($array);
$final_array = array_combine($match_count,$array_unique);
foreach($final_array as $key => $value) {
for($i = 0; $i < $key; $i++) {
echo 'addOrder(affiliate_id = ' . $value . ') . '<br>';
}
}
the functions
function unique_array($array) {
return array_unique($array, SORT_NUMERIC);
}
function arrayDuplicate($array) {
return array_unique(array_diff_assoc($array,array_unique($array)));
}
function array_value_count($match, $array) {
$count = 0;
foreach ($array as $key => $value)
{
if ($value == $match)
{
$count++;
}
}
return $count;
}
to fix the duplicates breaking the code I have tried this
if(count($array_unique) - count($match_count_unique) == 1 ) // do something
or
if(count($array_unique) != count($match_count_unique) == 1 ) // do something
How would I know where to add the missing duplicate value count and array items correctly without them getting out of sync? OR Is there a better way of doing this?
Taken from How do I count occurrence of duplicate items in array
$array = array(12,43,66,21,56,43,43,78,78,100,43,43,43,21);
$vals = array_count_values($array);
echo 'No. of NON Duplicate Items: '.count($vals).'<br><br>';
print_r($vals);
Result
No. of NON Duplicate Items: 7
Array
(
[12] => 1
[43] => 6
[66] => 1
[21] => 2
[56] => 1
[78] => 2
[100] => 1
)
Duplicate items = (Array Size) - (Total Number of Unique Values)
<?php
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14);
// get an array whose keys are the aff# and
//the values are how many times they occur
$dupes = array();
foreach ($affiliates as $aff) {
$dupes[$aff]++;
}
// remove the 1's since those aren't dupes
$dupes = preg_grep('~^1$~',$dupes,PREG_GREP_INVERT);
// de-dupe the original array
$affiliates = array_unique($affiliates);
// for each duped affiliate...
foreach ($dupes as $aff => $affCount) {
// for each time it was duped..
for ($c=0;$c<$affCount;$c++) {
// do something. $aff is the aff# like 11
}
}
?>
I have an array which comes from a report.
This report has info similar to:
157479877294,OBSOLETE_ORDER,obelisk,19/01/2013 01:42pm
191532426695,WRONG_PERFORMANCE,g3t1,19/01/2013 01:56pm
159523681637,WRONG_PERFORMANCE,g3t1,19/01/2013 01:57pm
176481653889,WRONG_PERFORMANCE,g4t1,19/01/2013 01:57pm
167479810401,WRONG_PERFORMANCE,g4t1,19/01/2013 02:00pm
172485359309,WRONG_PERFORMANCE,g4t2,19/01/2013 02:02pm
125485358802,WRONG_PERFORMANCE,g4t2,19/01/2013 02:02pm
172485359309,DAY_LIMIT_EXCEEDED,obelisk,19/01/2013 02:03pm
125485358802,DAY_LIMIT_EXCEEDED,obelisk,19/01/2013 02:03pm
What I need to do is get the total of each type of error and the location so for the first would be error: 'OBSOLETE_ORDER' and location: 'obelisk'. I have tried to do this a number of ways but the best I can come up with is a multi dimensional array:
$error_handle = fopen("$reportUrl", "r");
while (!feof($error_handle) )
{
$line_of_text = fgetcsv($error_handle, 1024);
$errorName = $line_of_text[1];
$scannerName = $line_of_text[2];
if($errorName != "SCAN_RESULT" && $errorName != "" && $scannerName != "SCAN_LOGIN" && $scannerName != "")
{
$errorsArray["$errorName"]["$scannerName"]++;
}
}
fclose($error_handle);
print_r($errorsArray);
gives me the following:
Array ( [OBSOLETE_ORDER] => Array ( [obelisk] => 1 ) [WRONG_PERFORMANCE] => Array ( [g3t1] => 2 [g4t1] => 2 [g4t2] => 2 ) [DAY_LIMIT_EXCEEDED] => Array ( [obelisk] => 2 ) )
which is great...except how do I then take that apart to add to my sql database?! (I am interested in getting the key and total of that key under the key the array is under)
and then add it to the tables
-errors-
(index)id_errors
id_event
id_access_scanner
id_errors_type
total_errors
-errors_type-
(index)id_errors_type
name_errors_type
-access_scanner-
(index)id_access_scanner
id_portal
name_access_scanner
PLEASE HELP!
Thanks!
A multidimensional array is more than you need. The approach to take is to create your own string ($arrayKey in my example) to use as an array key that combines the scanner name and the error so that you can get a count.
//this is the array containing all the report lines, each as an array
$lines_of_text;
//this is going to be our output array
$errorScannerArray = array();
//this variable holds the array key that we're going to generate from each line
$arrayKey = null;
foreach($lines_of_text as $line_of_text)
{
//the array key is a combination of the scanner name and the error name
//the tilde is included to attempt to prevent possible (rare) collisions
$arrayKey = trim($line_of_text[1]).'~'.trim($line_of_text[2]);
//if the array key exists, increase the count by 1
//if it doesn't exist, set the count to 1
if(array_key_exists($arrayKey, $errorScannerArray))
{
$errorScannerArray[$arrayKey]++;
}
else
{
$errorScannerArray[$arrayKey] = 1;
}
}
//clean up
unset($line_of_text);
unset($arrayKey);
unset($lines_of_text);
//displaying the result
foreach($errorScannerArray as $errorScanner => $count)
{
//we can explode the string hash to get the separate error and scanner names
$names = explode('~', $errorScanner);
$errorName = $names[0];
$scannerName = $names[1];
echo 'Scanner '.$scannerName.' experienced error '.$errorName.' '.$count.' times'."\n";
}
$list = array();
foreach ($lines as $line) {
$values = explode(',' $line);
$error = $values[1];
$scanner = $values[2];
if (!isset($list[$error])) {
$list[$error] = array();
}
if (!isset($list[$error][$scanner])) {
$list[$error][$scanner] = 1;
} else {
$list[$error][$scanner]++;
}
}
To go through each result I just did the following:
foreach ($errorsArray as $errorName=>$info)
{
foreach ($info as $scannerName=>$total)
{
print "$errorName -> $scannerName = $total </br>";
}
}
and now will just connect it to the sql
With your edited question, this much simpler loop will work for you, you just need to then insert the data into your database inside the loop, instead of echoing it out:
$errorsArray = Array (
[OBSOLETE_ORDER] => Array (
[obelisk] => 1
)
[WRONG_PERFORMANCE] => Array (
[g3t1] => 2
[g4t1] => 2
[g4t2] => 2
)
[DAY_LIMIT_EXCEEDED] => Array (
[obelisk] => 2
)
)
foreach($errorsArray as $row => $errors) {
foreach($errors as $error => $count) {
echo $row; // 'OBSOLETE_ORDER'
echo $error; // 'obelisk'
echo $count; // 1
// insert into database here
}
}
OLD ANSWER
You just need a new array to hold the information you need, ideally a count.
Im assuming that the correct data format is:
$report = [
['157479877294','OBSOLETE_ORDER','obelisk','19/01/2013 01:42pm'],
['191532426695','WRONG_PERFORMANCE','g3t1','19/01/2013 01:56pm'],
['159523681637','WRONG_PERFORMANCE','g3t1','19/01/2013 01:57pm'],
['176481653889','WRONG_PERFORMANCE','g4t1','19/01/2013 01:57pm'],
.....
];
foreach($report as $array) {
$errorName = $array[1];
$scannerName = $array[2];
if(exists($errorsArray[$errorName][$scannerName])) {
$errorsArray[$errorName][$scannerName] = $errorsArray[$errorName][$scannerName] + 1;
}
else {
$errorsArray[$errorName][$scannerName] = 1;
}
}