Multidimensional array loop with array search - php

I realize there are a number of questions about multidimensional arrays and foreach loops, and I have spent hours reading through them and trying to get my own loop to work -- without success. If the solution is a duplicate, I'll remove my question (or link to another if that is preferred).
Now, the challenge:
Using an array of returned MYSQL results. The results are from multiple joined tables in an associative array. Now I need to convert it to the multidimensional array I need.
I've got most of it working, but my issue is looping through and adding new items to the right place in the array.
Here's some code:
//example of how array is setup in the way I want, this part works.
foreach($results as $i => $a):
//some other code is here, see below.
$items[$i] = [
"id" => $a['id'],
"itemid" => $a['itemid'],
"name" => $a['name'],
"def" => $a['def'],
"class" => $a['class'],
"timeline" => $a['timeline'],
"files" => [
[0] => [
"id" => $a['fileid'],
"name" => $a['filename'],
"path" => $a['filepath'],
"type" => $a['filetype']
]
],
"tags" => [
[0] => [
"id" => $a['tagid'],
"name" => $a['tagname']
]
]
];
endforeach;
Then I've tried a number of ways to loop through in order to only add to the 'tags' or 'files' if the item 'id' is the same as the last. Here is the current code in my editor, not working:
//inside foreach loop, before above code
if($items[$i-1]['id'] == $a['id']):
//it is the same item, works to here.
if(in_array($a['filename'], $items[$i-1], FALSE)):
//add to files array for last item
$items[$i-1]['files'][] = [
"id" => $a['fileid'],
"name" => $a['filename'],
"path" => $a['filepath'],
"type" => $a['filetype']
];
elseif(in_array($a['tagname'], $items[$i-1], FALSE)):
//add to tags array for last item
$items[$i-1]['tags'][] = [
"id" => $a['tagid'],
"name" => $a['tagname']
];
endif;
else:// else it does the code above
As you can see, my most recent attempt was to use in_array, which I now realize doesn't work on multidimensional arrays. My issue is that I can't figure out how to determine if its a new file or new tag for the same item.
Ultimately, I want an array of 'items' which have multiple 'files' and 'tags.' I'm going to json_encode and use it with JS afterwards.
Any advice on how to get this working or optimize it, would be greatly appreciated.
P.S. As I mentioned above, I know this question has been asked before -- though I wasn't able to get their solutions working for me. I'll remove this question if the solution is a duplicate (as in, it's not really helpful to others). Thank you for any help, it is greatly appreciated!

Don't use "autoincrementing" array indices as they easily get messed up. Use your database id since it's already there:
//example of how array is setup in the way I want, this part works.
foreach($results as $i => $a):
$items[$a['id']] = [ // THIS CHANGED.
"id" => $a['id'],
"itemid" => $a['itemid'],
...
Now, with any further result, you can easily check, if the id is already in your array:
if (isset($items[$a['id']])) {
// We had this id before, just add files/tags to it.
// Check first, if files subarray exists, if not: create it.
if (!isset($items[$a['id']]['files'])) {
$items[$a['id']]['files'] = array();
}
$items[$a['id']]['files'][] = array(...); // add the new file.
// Repeat for tags.
}
If your result could return the same file more than once for an id, you can check if the filename is already in there by using a search function:
$filename = $a['filename'];
if (!searchFilename($filename, $items[$a['id']]['files']) {
// Filename is not in there, yet.
// Add file info.
}
function searchFilename($id, $array) {
foreach ($array as $key => $val) {
if ($val['filename'] === $id) {
return true;
}
}
return false;
}
Same applies to tags in a similar way.
In the end, if you do not want the ids for index of $items, just call:
$items = array_values($items);

Related

how to process my array to be associative on first two levels and on third to have simple value

I'm trying to construct an array where there only strings and the array would look like this
key->key->value
To explain it I attached two screenshots below.
I start with this:
After my code below I'm 90% there, yet there is an array in value on the third level instead of simple value.
Here is some code:
$theme = ThemeHandler::with('sections.settings')->find($activeTheme);
$themeSettings = $theme->sections;
$themeSettings = collect($themeSettings->toArray());
// dd($themeSettings);
$themeSections = [];
foreach ($themeSettings as $key => $value) {
$settings = collect($value['settings']);
$settings = $settings->mapToGroups(function ($item) {
return [$item['key'] => $item['value']];
});
$themeSections[$value['key']] = $settings->toArray();
}
dd($themeSections);
I would like to end up with this structure
key->key->value
and not
key->key->single_element_array->value
I'm not sure how I end up with an array at the bottom level when I do this
return [$item['key'] => $item['value']];
inside the mapToGroups, which is a function found here: https://laravel.com/docs/5.8/collections#method-maptogroups
Maybe I misunderstand how mapToGroups work. Anybody has an idea how to get key->key->value structure output?
Use mapWithKeys() instead of mapToGroups().
You're getting an array instead of the simple value you expect because the value is a group, albeit a group with only one member.
mapToGroups() groups all the values with the same key, but mapWithKeys() will assign a single value to each key.
You can see in the examples in the collection documentation, mapToGroups() produces a result like this:
[
'Sales' => ['John Doe', 'Jane Doe'],
'Marketing' => ['Johnny Doe'],
]
And mapWithKeys() result is like this:
[
'john#example.com' => 'John',
'jane#example.com' => 'Jane',
]

How to apply a filter to update element in array of arrays?

I am trying to create a PHP application that lets me uploading data to a MongoDB collection. For that I have "installed" the PHP driver with no problems.
However, I cannot find anyway -neither in the PHP guide- how could I update an element in an array of arrays.
Collection structure
As you may see, _unidades is an array of arrays. Each of those arrays contains an ID, a string and another array. In my case, which will be chosen depends on previous param -it has to match with element 1 of one of them.
Once I have selected that structure, I want to insert a new array into its array of arrays (position 2).
Regarding code, I tried the following:
$bulk = new MongoDB\Driver\BulkWrite();
$bulk->update(
[
'_isbn' => (int) $_POST["isbn"],
'_topics' =>
[
'0' => (int) $_POST["topic"]
]
],
[
'$set' => [
'1' => array($_POST["sentence"], $_POST["question"])
]
]
);
$writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY, 100);
$resultado = $manager->executeBulkWrite('libreee.faq', $bulk, $writeConcern);
However as you can see I am not capable to determine at least that it doesn't have to be an specific array (7th line).
Once said that I look forward to receiving your help.
Thanks a lot in advance.
Regards,
Ciconia.

PHP output array with identical identifiers

I will need to output an array something along the lines of
[
0 => ['text' => 'category'],
1 => ['text' => category']
]
So basicly an array for each category in my database. I need to output them like this because of how they will be exported to another site. (I cannot foreach inside the export)
My current code, is a foreach loop that runs through my categories. If i var_dump my field variable inside the foreach loop i get the result i want, but as mentioned i need to export everything in the format like above out of the foreach loop.
Code:
foreach ($categories as $category) {
$fieldvalue = ['text' => $category->categoryname];
}
What i have tried:
Putting the array in a string to explode outside the loop-
Result: Because of the "same identifier" issue i could only export the last result
What i need to be done:
My current code outputs the array as a text string so basicly
$fieldvalue ? "['text' => '$category->categoryname']";
And my output will be
array:4 [
0 => "['text' => value1]"
1 => "['text' => value2]"
2 => "['text' => value3]"
3 => "['text' => value4]"
]
I just need to get the string to be an array.
Try:
foreach ($categories as $category) {
$fieldvalue[] = ['text' => $category->categoryname];
}
You want the result to be an array that contains many arrays (multi-dimensional array). Using $var[] = $something adds that $something as an array object.

How to replace an element in an array

I have a COLLECTION collflokks in MongoDB, sample Document is :-
{
"_id" : "b_8AUL",
"f_name" : "Pizza. Hut",
"f_lat" : "22.7523513",
"f_lng" : "75.9225847",
"c_uid" : "33",
"f_type" : NumberLong(3),
"members" : [
"42",
"43"
]
}
Within the "members" array , I want to add Arrays like {id:42,name:Mark} , {id:43,name:Hughes}
Currently i'm adding just ids(eg.42,43). I'm only concerned about the new data as it will have new ids .Please suggest.
Earlier I was using this code to push into the members Array:
$flokkCollection = 'collFlokks';
$flokkCollection->update(
array("_id" => $f_handle),
array('$push' => array("members" => $u_id))
);
Well if what you are asking here is "replacing your existing data" then you need to "loop" the results from the collection and "replace" the array content that exists with your new format.
There are likely smarter ways to approach this, but you are not really giving us all the required information in your question, so I can only answer in the basic terms.
Presuming you have:
$required = array(
array(array("id" => "42"), array("name" => "Mark")),
array(array("id" => "43"), array("name" => "Hughes"))
);
As input, then you do something like this:
function myMapper($v) {
return $v["id"];
}
$mapped = array_map("myMapper",$required);
foreach( $mapped as $value) {
$filtered = array_values(
array_filter($required,function($k) {
return $k["id"] == $value;
})
)[0];
collection.update(array(
array("members" => $value),
array('$set' => array(
"members.$" => $filtered
))
));
}
Which should use the positional $ operator to find the matched "position" of the array element by the value used in the "query" portion of the update statement, then in the "update" portion of that statement $set that current array index to the new value at the "filtered" content index from the original input array.
Outside of PHP. We call these inner elements "objects" and not "arrays" which is a PHP notation trait. Key/value things are "objects" and "lists" are "arrays".

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.

Categories