Incorrect PHP sizeof or count [closed] - php

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
The following is a print_r of an array converted from an XML document:
Array
(
[layer1] => Array
(
[item] => Array
(
[item-id] => 1886731
[item-name] => Bad Dog
[category] => pets
[link] = http://www.baddog.com/
)
[total-matched] => 1
)
)
For the above array, sizeof($myarray[layer1][item]) should return 1, but it returns 4. I get the correct number of "item" items if there are more than one of them. The same error happens regardless of whether I use "sizeof" or "count". How do I get PHP to return "1" when there is only one item?
Consequently, if there is one item, I can't access item-name using array[layer1][item][0][item-name], I have to use array[layer1][item][item-name].

There is a world of difference between:
$array1 = array(
'layer1' => array(
'item' => array(
'0' => array(
'item-id' => 123123,
'item-name' => 'Bad Dog',
'category' => 'pets',
'link' => 'http://www.baddog.com/',
),
)
)
);
and:
$array2 = array(
'layer1' => array(
'item' => array(
'item-id' => 123123,
'item-name' => 'Bad Dog',
'category' => 'pets',
'link' => 'http://www.baddog.com/',
)
)
);
Basically they are different structures, and PHP is correct to return the following:
count($array1['layer1']['item']);
/// = 1 (count of items in the array at that point)
count($array2['layer1']['item']);
/// = 4 (count of items in the array at that point)
If you wish to get a count for ['layer1']['item'] that makes sense to your app, you will always need ['layer1']['item'] to be an array containing multiple array structures... i.e. like $array1 above. This is the reason why I ask what is generating your array structure because - whatever it is - it is basically intelligently stacking your array data depending on the number of items, which is something you don't want.
Code that can cause an array to stack in this manner normally looks similar to the following:
/// $array = array(); /// this is implied (should be set elsewhere)
$key = 'test';
$newval = 'value';
/// if we are an array, add a new item to the array
if ( is_array($array[$key]) ) {
$array[$key][] = $newval;
}
/// if we have a previous non-array value, convert to an array
/// containing the previous and new value
else if ( isset($array[$key]) ) {
$array[$key] = array($array[$key],$newval);
}
/// if nothing is set, set the $newval
else {
$array[$key] = $newval;
}
Basically if you keep calling the above code, and tracing after each run, you will see the following structure build up:
$array == 'value';
then
$array == array(0 => 'value', 1 => 'value');
then
$array == array(0 => 'value', 1 => 'value', 2 => 'value');
It's the first step in this process that is causing the problem, the $array = 'value'; bit. If you modify the code slightly you can get rid of this:
/// $array = array(); /// this is implied (should be set elsewhere)
$key = 'test';
$newval = 'value';
/// if we are an array, add a new item to the array
if ( is_array($array[$key]) ) {
$array[$key][] = $newval;
}
/// if nothing is set, set the $newval as part of an subarray
else {
$array[$key] = array($newval);
}
As you can see all I've done is delete the itermediate if statement, and made sure when we discover no initial value is set, that we always create an array. The above will create a structure you can always count and know the number of items you pushed on to the array.
$array == array(0 => 'value');
then
$array == array(0 => 'value', 1 => 'value');
then
$array == array(0 => 'value', 1 => 'value', 2 => 'value');
update
Ah, I thought so. So the array is generated from XML. In this case I gather you are using a predefined library to do this so modifying the code is out of the question. So as others have already stated, your best bet is to use one of the many XML parsing libraries available to PHP:
http://www.uk.php.net/simplexml
http://www.uk.php.net/dom
When using these systems you retain more of an object structure which should be easier to count. Both the above also support xpath notation which can allow you to count items without even having to grab hold of any of the data.
update 2
Out of the function you've given, this is the part that is causing your arrays to stack in the manner that is causing the problem:
$children = array();
$first = true;
foreach($xml->children() as $elementName => $child){
$value = simpleXMLToArray($child,$attributesKey, $childrenKey,$valueKey);
if(isset($children[$elementName])){
if(is_array($children[$elementName])){
if($first){
$temp = $children[$elementName];
unset($children[$elementName]);
$children[$elementName][] = $temp;
$first=false;
}
$children[$elementName][] = $value;
}else{
$children[$elementName] = array($children[$elementName],$value);
}
}
else{
$children[$elementName] = $value;
}
}
The modification would be:
$children = array();
foreach($xml->children() as $elementName => $child){
$value = simpleXMLToArray($child,$attributesKey, $childrenKey,$valueKey);
if(isset($children[$elementName])){
if(is_array($children[$elementName])){
$children[$elementName][] = $value;
}
}
else{
$children[$elementName] = array($value);
}
}
That should stop your arrays from stacking... however if you have any other part of your code that was relying on the previous structure, this change may break that code.

Related

PHP: can't add to empty associative array [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
i'm missing something simple here trying to add a new key/value pair and then add to the value of that pair as this loop goes on. this throws an undefined index error:
$someAssocArray = [2] => 'flarn'
[3] => 'turlb'
$someOtherAssocArray = [0] => 'id' => '11'
'name' => 'flarn'
[1] => 'id' => '22'
'name' => 'turlb'
[2] => 'id' => '33'
'name' => 'turlb'
[3] => 'id' => '44'
'name' => 'flarn'
$idList = [];
foreach($someAssocArray as $key=>$value) {
foreach($someOtherAssocArray as $item) {
if($item['name'] === $value) {
$idList[$value] += $item['id'];
}
}
}
the end result of idList should look like this:
$idList = [ "flarn" => "11,44"
"turlb" => "22,33" ]
so please tell me what i'm missing so i can facepalm and move on.
[Edit] OK I just re-read that question and I might be misunderstanding. Is the desired output of 11,44 supposed to represent the sum of the values? Or a list of them?
This code will generate a warning if $idList[$value] doesn't exist:
$idList[$value] += $item['id'];
This is happening because it has no value yet for the first time you're incrementing. You can avoid this issue by initializing the value to zero for that first time when it doesn't exist:
If you want a sum of the values:
if($item['name'] === $value) {
$idList[$value] ??= 0; // initialize to zero
$idList[$value] += $item['id']; // add each value to the previous
}
If you want a list of the values:
if($item['name'] === $value) {
$idList[$value] ??= []; // initialize to an empty array
$idList[$value][] = $item['id']; // append each element to the list
}
(Then you can use implode() to output a comma-separated string if desired.)

Restructuring Multi Dimensional Array Format

I am struggling with what would appear to be a pretty straight forward task. I have looked at and tried all kinds of functions and suggestion on SO hoping that maybe there is something simple and functional out there. Nothing I tried gives me the logic to do the restructuring.
I have a long complex array. However very much simplified the logic problem I am trying to solve generically is as follows:
$cost_type = Array
(
0 => "ISP2",
1 => "ISP3",
2 => "ISP4"
);
$supplier_name = Array
(
0 => "NAME-A",
1 => "NAME-B",
2 => "NAME-C"
);
$propertyid = Array
(
0 => "property1",
1 => "property2",
2 => "property2"
);
and I need to convert it to the following set of arrays (noting the concatenation of the two arrays with a common property id.....
$property1
(
array['charges']
[0] =>IPS2
array ['names']
[0] =>NAME-A
)
$property2
(
array['charges']
[0] ->IPS3
[1] =>IPS4
array['names']
[0] =>NAME-B
[1] =>NAME-c
)
I have tried everything over the course of the last few hours and a simple solution totally evades me.
If you can join the three arrays as you say in comments above this code will generate the look you want.
I loop through the array with property and keep key as the key to find names and charges in the other subarrays.
$cost_type = Array
(
0 => "ISP2",
1 => "ISP3",
2 => "ISP4"
);
$supplier_name =Array
(
0 => "NAME-A",
1 => "NAME-B",
2 => "NAME-C"
);
$propertyid = Array
(
0 => "property1",
1 => "property2",
2 => "property2"
);
$arr[] = $cost_type;
$arr[] = $supplier_name;
$arr[] = $propertyid;
$result = array();
Foreach($arr[2] as $key => $prop){
$result[$prop]["charges"][] =$arr[0][$key];
$result[$prop]["names"][] = $arr[1][$key];
}
Var_dump($result);
https://3v4l.org/EilvE
The following code converts the original array in the expected result:
$res = array();
foreach($arr[2] as $k => $foo){ // foreach property
if(!isset($res[$foo])){ // add property if not yet in list
$res[$foo] = array(
'charges' => array($arr[0][$k]),
'names' => array($arr[1][$k])
);
}else{ // add new value to already existing property
$res[$foo]['charges'][] = $arr[0][$k];
$res[$foo]['names'][] = $arr[1][$k];
}
}
Check it out here: https://eval.in/904473
Of course, it assumes a bunch on things about the data, but it should work for any number of items.
And if you need the property in another variable, just access it with $res['name of it].
Run this code you will get smiler result as you want :
$twodimantion=array();
$properties=array('property1','property2','property3');
$charges=array('ISP2','ISP3','ISP4');
$names=array('NAME-A','NAME-B','NAME-C');
foreach ($properties as $key => $property) {
$twodimantion['charge'][$key]=$charges[$key];
$twodimantion['names'][$key]=$names[$key];
$twoarray[$property]=$twodimantion;
}
echo '<pre>';
print_r($twoarray);
echo '</pre>';
I can't say I completely follow what you are trying to do, but I think this may be part of the way there for you.
When trying to restructure data in PHP, it's often helpful to create a empty array (or other data structure) to store the new data in first. Then you find a way to loop over your initial data structure that allows you to insert items into your reformatted structure in the right sequence.
<?php
$properties = []; // Array to hold final result
// Loop over your initial inputs
foreach ($groupsOfValues as $groupName => $groupValues) {
$temp = []; // Array to hold each groupings reformatted data
// Loop over the items in one of the inputs
for ($i=0; $i<count($group) && $i<count($properties)+1; $i++) {
if (!is_array($temp[$groupName])) {
$temp[$groupName] = [];
}
$temp[$groupName][] = $group[$i];
}
$properties[] = $temp;
}

Modify Current Value of Multidimensional Array

I have three arrays, say multiarray, valsarray, and otherarray. otherarray is a multidimensional array that supplies values to multiarray and valsarray, but besides that it is unimportant here. valsarray takes values from a subarray of each value in otherarray and multiarray takes straight values from otherarray, as demonstrated below:
foreach($otherarray as $other){
foreach($other as $sub){
$valsarray[] = $sub
}
$multiarray[] = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
}
Now what I would like to do is append each key/value pair in valsarray to the current array entry of multiarray, to achieve a result similar to:
$multiarray = array('Val1' => $other['Val1'], 'Val2' => $other['Val2'],
'VALSARRAY_KEY1' => VALSARRAY_VALUE1, ..., 'VALSARRAY_KEYN' => VALSARRAY_VALUEN)
I have attempted to solve this using current in the following fashion:
foreach($valsarray as $key => $val){
current($multiarray)[$key] = $val;
}
But the multiarray remained unaltered. I may be misunderstanding how current works, or how to approach this problem, so any help or direction would be appreciated.
EDIT- EXAMPLE
otherarray = array(...prior array entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
'Val3' => 'not important',
'Val4' => array(0 => 'subA', 1 => 'subB'),
...next array entries...);
BEFORE MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde'));
valsarray = array(0 => 'subA', 1 => 'subB');
AFTER MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
0 => 'subA',
1 => 'subB'));
So if multiarray was a regular array instead of a multidimensional one, I would do something like:
foreach($valsarray as $key => $val){
$multiarray[$key] = $val;
}
To achieve the end result.
I am not 100% sure what you are trying to accomplish a Minimal, Complete, and Verifiable example may help if I have misunderstood something.
It appears that the current() function does not work as you assume. (Or more specifically, the internal pointer.)
If you look at the example in the PHP documentation: Current(), you will see that for current($array) to change elements, you need to call next($array) or prev($array).
These function move the internal pointer of the array.
Note that in PHP 5, foreach loops use the internal pointer (and reset it when you start a loop), but in PHP 7, foreach loops do not use the internal pointer.
Anyway, here is my best guess at what could help you.
$valsarray_index = 0;
foreach ($otherarray as $other) {
$multiarray_value = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
foreach ($other as $sub) {
$multiarray_value[$valsarray_index] = $sub;
// $multiarray_value["VALSARRAY_KEY" . $valsarray_index] = $sub;
$valsarray[] = $sub;
$valsarray_index += 1; // This stays in lockstep with the last index of $valsarray
}
$multiarray[] = $multiarray_value;
}
I am not exactly sure about what you want the final output to look like. If this produces incorrect information, then if would be helpful to provide some specific arrays for input and what you expect as output.

PHP Array needs fixed (consolidated/merged)

I have this multidimensional array which I'll name "original":
$original=
array
0 =>
array
'animal' => 'cats'
'quantity' => 1
1 =>
array
'animal' => 'dogs'
'quantity' => '1'
2 =>
array
'animal' => 'cats'
'quantity' => '3'
However, I want to merge internal arrays with the same animal to produce this new array (with quantities combined):
$new=
array
0 =>
array
'animal' => 'cats'
'quantity' => 4
1 =>
array
'animal' => 'dogs'
'quantity' => '1'
I understand that there are similar questions on stackoverflow, but not similar enough for me to be able to figure out how to use the feedback those questions have gotted to apply to this specific example. Yes, I know I probably look stupid to a lot of you, but please remember that there was a time when you too didn't know crap about working with arrays :)
I've tried the following code, but get Fatal error: Unsupported operand types (Referring to line 11). And if I got that error to go away, I'm not sure if this code would even produce what I'm trying to achieve.
$new = array();
foreach($original as $entity){
if(!isset($new[$entity["animal"]])){
$new[$entity["animal"]] = array(
"animal" => $entity["animal"],
"quantity" => 0,
);
}
$new[$entity["animal"]] += $entity["quantity"];
}
So, I don't know what I'm doing and I could really use some help from the experts.
To try to give a super clear question, here goes... What changes do I need to make to the code so that it will take $original and turn it into $new? If the code I provided is totally wrong, could you provide an alternative example that would do the trick? Also, the only language I am familiar with is PHP, so please provide an example using only PHP.
Thank you
You're very close.
$new[$entity["animal"]] += $entity["quantity"];
needs to be
$new[$entity["animal"]]['quantity'] += $entity["quantity"];
In your if ( !isset [...] ) line, you're setting $new[$entity['animal']] to an array, so you need to access the 'quantity' field of that array before trying to add the new quantity value to it.
One of the reasons why your code is not working is that you're using the animal name as the array index, not the integer index which is used in your desired output.
Try this:
$new = array(); // Desired output
$map = array(); // Map animal names to index in $new
$idx = 0; // What is the next index we can use
foreach ($original as $entity) {
$animal = $entity['animal'];
// If we haven't saved the animal yet, put it in the $map and $new array
if(!isset($map[$animal])) {
$map[$animal] = $idx++;
$new[$map[$animal]] = $entity;
}
else {
$new[$map[$animal]]['quantity'] += $entity['quantity'];
}
}
This works:
$new = array();
$seen = array();
foreach($original as $entity) {
// If this is the first time we're encountering the animal
if (!in_array($entity['animal'], $seen)) {
$new[] = $entity;
$seen[] = $entity['animal'];
// Otherwise, if this animal is already in the new array...
} else {
// Find the index of the animal in the new array...
foreach($new as $index => $new_entity) {
if ($new_entity['animal'] == $entity['animal']) {
// Add to the quantity
$new[$index]['quantity'] += $entity['quantity'];
}
}
}
}
Your example was using the animal name as the index, yet the actual index is just an integer.
However, I think the resulting array would be easier to use and easier to read if it was formatting like this instead:
array('cats' => 4, 'dogs' => 1)
That would require different but simpler code than above... but, it wouldn't be a direct response to your question.

Understanding the basics of multidimensional arrays

I am new to using multidimensional arrays with php, I have tried to stay away from them because they confused me, but now the time has come that I put them to good use. I have been trying to understand how they work and I am just not getting it.
What I am trying to do is populate results based on a string compare function, once I find some match to an 'item name', I would like the first slot to contain the 'item name', then I would like to increment the priority slot by 1.
So when when I'm all done populating my array, it is going to have a variety of different company names, each with their respective priority...
I am having trouble understanding how to declare and manipulate the following array:
$matches = array(
'name'=>array('somename'),
'priority'=>array($priority_level++)
);
So, in what you have, your variable $matches will point to a keyed array, the 'name' element of that array will be an indexed array with 1 entry 'somename', there will be a 'priority' entry with a value which is an indexed array with one entry = $priority_level.
I think, instead what you probably want is something like:
$matches[] = array(name => 'somename', $priority => $priority_level++);
That way, $matches is an indexed array, where each index holds a keyed array, so you could address them as:
$matches[0]['name'] and $matches[0]['priority'], which is more logical for most people.
Multi-dimensional arrays are easy. All they are is an array, where the elements are other arrays.
So, you could have 2 separate arrays:
$name = array('somename');
$priority = array(1);
Or you can have an array that has these 2 arrays as elements:
$matches = array(
'name' => array('somename'),
'priority' => array(1)
);
So, using $matches['name'] would be the same as using $name, they are both arrays, just stored differently.
echo $name[0]; //'somename';
echo $matches['name'][0]; //'somename';
So, to add another name to the $matches array, you can do this:
$matches['name'][] = 'Another Name';
$matches['priority'][] = 2;
print_r($matches); would output:
Array
(
[name] => Array
(
[0] => somename
[1] => Another Name
)
[priority] => Array
(
[0] => 1
[1] => 2
)
)
In this case, could this be also a solution with a single dimensional array?
$matches = array(
'company_1' => 0,
'company_2' => 0,
);
if (isset($matches['company_1'])) {
++$matches['company_1'];
} else {
$matches['company_1'] = 1;
}
It looks up whether the name is already in the list. If not, it sets an array_key for this value. If it finds an already existing value, it just raises the "priority".
In my opinion, an easier structure to work with would be something more like this one:
$matches = array(
array( 'name' => 'somename', 'priority' => $priority_level_for_this_match ),
array( 'name' => 'someothername', 'priority' => $priority_level_for_that_match )
)
To fill this array, start by making an empty one:
$matches = array();
Then, find all of your matches.
$match = array( 'name' => 'somename', 'priority' => $some_priority );
To add that array to your matches, just slap it on the end:
$matches[] = $match;
Once it's filled, you can easily iterate over it:
foreach($matches as $k => $v) {
// The value in this case is also an array, and can be indexed as such
echo( $v['name'] . ': ' . $v['priority'] . '<br>' );
}
You can also sort the matched arrays according to the priority:
function cmp($a, $b) {
if($a['priority'] == $b['priority'])
return 0;
return ($a['priority'] < $b['priority']) ? -1 : 1;
}
usort($matches, 'cmp');
(Sourced from this answer)
$matches['name'][0] --> 'somename'
$matches['priority'][0] ---> the incremented $priority_level value
Like David said in the comments on the question, it sounds like you're not using the right tool for the job. Try:
$priorities = array();
foreach($companies as $company) {
if (!isset($priorities[$company])) { $priorities[$company] = 0; }
$priorities[$company]++;
}
Then you can access the priorities by checking $priorities['SomeCompanyName'];.

Categories