There's a thing that I don't understand when I use array_merge() :
$defaultOptions = [
'active' => null,
'activeClass' => 'active',
'wrapper' => [
'attributes' => null,
'templateVars' => null
],
'item' => [
'hasChildrenClass' => '', // this disappears after array_merge
'attributes' => null,
'linkAttrs' => null,
'templateVars' => null
]
];
$options = [
'active' => [5,3],
'item' => [
'attributes' => ['class' => 'test']
]
];
$options = array_merge($defaultOptions, $options);
The result of $options is
[
'active' => [
(int) 0 => (int) 5,
(int) 1 => (int) 3,
],
'activeClass' => 'active',
'wrapper' => [
'attributes' => null,
'templateVars' => null,
],
'item' => [
'attributes' => [
'class' => 'test',
],
],
]
I don't understand why $options['item']['hasChildrenClass'] disappeared in my result ?
In array_merge() if the arrays have the same string keys, then values from the later arrays will overwrite the previous one. If the arrays have numeric keys, then values from the later arrays will appended with previous one. If the arrays contain null or empty values, this value will be skipped and removed from the merged array.
Read more at php.net manual
Related
I have a nested array with a structure as detailed below.
It is an org chart where an employ could have other employees related:
$tree_array = [
[
'id' => 1,
'employee' => 'John',
'leader_id' => NULL,
'team' => [
[
'id' => 2,
'employee' => 'Maria',
'leader_id' => 1,
'team' => [],
],
[
'id' => 3,
'employee' => 'Kevin',
'leader_id' => 1,
'team' => [
[
'id' => 4,
'employee' => 'Alan',
'leader_id' => 3,
'team' => [],
],
[
'id' => 5,
'employee' => 'Bret',
'leader_id' => 3,
'team' => [],
],
],
],
],
]
];
Every node has an ID and a team, that could be an array of other nodes and so on.
I need to obtain each node in a flat array, like this structure:
$flat_array = array(
array(
'id' => 1,
'employee' => 'John',
'leader_id' => NULL
),
array(
'id' => 2,
'employee' => 'Maria',
'leader_id' => 1
),
array(
'id' => 3,
'employee' => 'Kevin',
'leader_id' => 1
),
...
);
I've tried to implement a recursive function, but I get only the nodes of the last iteration.
Any help?
You can make a recursive function, like this:
function flatten($arr, $final=array()) {
foreach($arr as $a) {
$tmp = $a;
unset($tmp['team']);
$final[]= $tmp;
if ($a['team'] && count($a['team']) > 0) $final = flatten($a['team'], $final);
}
return $final;
}
$flat =flatten($tree_array);
Test it here: https://www.tehplayground.com/UHWCccFIkrS5v75Z
I have below array, which includes a set of default indexes, that must be present in my final array:
'entities' => [
'deliveredAt',
'issuedAt',
'totals' => [
'due',
'gross',
'net',
'tax' => [
'amount',
'net',
'rate',
],
]
],
The above array is saved in a variable called $entities.
Now, I have a 3rd party API, that will return the above entities, but only include them in the response if the entity contains a value.
For example, a $response can look like this:
array:2 [▼
"issuedAt" => "2020-08-20"
"totals" => array:1 [▼
"tax" => []
]
]
As you can see, if comparing the returned array with the indexes that I expect, there is a few missing:
deliveredAt
totals.due, totals.gross, totals.net, totals.tax.amount, totals.tax.net, totals.tax.rate
I am trying to make a method that can iterate over the $response array, and check if it contains the indexes that I expect. If not, I simply want to set the index with a value of null.
Below is what I have so far:
foreach ($entities as $key => $entity) {
if (!is_array($entity)) {
if (!isset($response[$entity])) {
$response[$entity] = null;
}
}
}
However, this will only add an index that is not an array. In this example, it will only add: deliveredAt => null.
How can I do, so the above method can iterate through multiple at least 2 nested arrays and add the index name and null value?
You can define initial array with keys and NULL (or whatever you need) as values:
$entities = [
'deliveredAt' => null,
'issuedAt' => null,
'totals' => [
'due' => null,
'gross' => null,
'net' => null,
'tax' => [
'amount' => null,
'net' => null,
'rate' => null,
],
]
];
// here's your real data
$realData = [
"issuedAt" => "2020-08-20",
"totals" => [
"tax" => [
'net' => 42,
]
]
];
// now use array_replace_recursive to replace keys in `$entities` with values of `$realData`
print_r(array_replace_recursive($entities, $realData));
Fiddle.
Also note that keys from $realData that do not exist in $entities will be added to result.
You can use array_replace_recursive for doing this. You had only to change your associative array entities a little bit, so every property needs a initiliasation (e.g. NULL or '').
$result = array_replace_recursive($entities, $array);
Here you can test it http://sandbox.onlinephpfunctions.com/code/4688ed3240050479edeef7c9e4da16f98dbe01de
Here is the hole code:
$array = [
"issuedAt" => "2020-08-20",
"totals" => [
"tax" => [
'amount' => 100
]
]
];
$entities = [
'deliveredAt' => NULL,
'issuedAt' => NULL,
'totals' => [
'due' => NULL,
'gross' => NULL,
'net' => NULL,
'tax' => [
'amount' => NULL,
'net' => NULL,
'rate' => NULL
],
]
];
$result = array_replace_recursive($entities, $array);
var_dump($result);
I have an array with 3 levels and I'd like to merge/flatten all 3rd level subarrays into one subarray/row on the 2nd level.
$array = [
[
'User' => [
'id' => 57341,
'updated' => null,
'userId' => 57341,
'value' => null,
'lat' => 53.4537812,
'lon' => -2.1792437,
],
[
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
],
[
'User' => [
'id' => 57336,
'updated' => null,
'userId' => 57336,
'value' => null,
'lat' => 53.473684,
'lon' => -2.2399827,
],
[
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
],
];
The deep User-keyed subarrays (having 6 elements) should be merged with its sibling/indexed subarray (having 3 elements) to form a 9-element row on the second level.
Desired result:
[
[
'id' => 57341,
'updated' => null,
'userId' => 57341,
'value' => null,
'lat' => 53.4537812,
'lon' => -2.1792437,
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
],
[
'id' => 57336,
'updated' => null,
'userId' => 57336,
'value' => null,
'lat' => 53.473684,
'lon' => -2.2399827,
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
]
You can use splat ... operator with array_merge
foreach($a as $child){
$flatten[] = array_merge(...$child);
}
Working example :- https://3v4l.org/HkUh6
To merge and flatten the 3rd level data sets into consolidated 2nd level rows with functional style programming, make iterated calls of array_merge() which receive all 3rd level payloads at once. The spread operator (...) is a concise technique used to unpack multiple elements in an array. A special consideration is needed for this case because spreading elements which have non-numeric keys will cause code breakage. To overcome this, simply call array_values() to "index" the array (replace all keys with sequenial numbers) before spreading.
Code: (Demo)
var_export(
array_map(
fn($rows) => array_merge(...array_values($rows)),
$array
)
);
Slightly different to your example output, but you can merge your inner arrays.
<?php
$data =
[
[
'animal' => [
'type' => 'fox'
],
[
'colour' => 'orange'
]
],
[
'animal' => [
'type' => 'panda'
],
[
'colour' => 'black and white'
]
]
];
$result =
array_map(
function($v) {
return array_merge($v['animal'], $v[0]);
},
$data
);
var_export($result);
Output:
array (
0 =>
array (
'type' => 'fox',
'colour' => 'orange',
),
1 =>
array (
'type' => 'panda',
'colour' => 'black and white',
),
)
If I understood you correctly you need to merge User array and in this case with the second array in that key. it that case something like this should work
foreach($array as $key=>$deep1){
$newArray = [];
foreach($deep1 as $deep2){
$newArray = array_merge($newArray,$deep2)
}
$array[$key] = $newArray;
}
Did II understand your question correctly?
I'm struggling to update a document and add new elements (fields) into it's existing array without losing the array elements on update.
This is my code which inserts a new document into the collection if it doesn't already exist (upsert):
$updateResult = $collection->findOneAndUpdate(
[
'recording-id' => $out['recording']->id
],
['$set' => [
'release' => [
'id' => $out['release']->id,
'title' => $out['release']->title,
'date' => $out['release']->date,
'country' => $out['release']->country
],
'artist' => [
'id' => $out['artist']->id,
'name' => $out['artist']->name,
],
'recording' => [
'id' => $out['recording']->id,
'title' => $out['recording']->title,
'score' => $out['recording']->score,
'length' => $out['recording']->length,
'release-count' => count($out['recording']->releases),
],
'release-group' => [
'id' => $out['release-group']['id'],
'title' => $out['release-group']['title'],
'first-release-date'=>$out['release-group']['first-release-date'],
'primary-type' => $out['release-group']['primary-type'],
'musicbrainz' => $out['release-group']['musicbrainz'],
'url-rels' => $out['release-group']['url-rels'],
'coverart' => $out['release-group']['coverart']
],
'execution' => [
'firstfind' => $out['execution']->time
]
]
],
['upsert' => true,
'projection' =>
[
'_id' => 0,
'release' => 1,
'artist' => 1,
'recording' => 1,
'release-group' => 1,
'execution' => 1
],
'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
]
);
So now I have an existing document in a collection:
{
"_id" : ObjectId("5d1a6aaf5ecc8001ee858f6c"),
"recording-id" : "d0d439f9-5324-4728-8706-2da39adb89c5",
"artist" : {
"id" : "9d97b077-b28d-4ba8-a3d9-c71926e3b2b6",
"name" : "Gordon Lightfoot"
},
"recording" : {
"id" : "d0d439f9-5324-4728-8706-2da39adb89c5",
"title" : "Sundown",
"score" : 100,
"length" : 184000,
"release-count" : 2
},
"release" : {
"id" : "0c008d76-2bc9-44a3-854b-0a08cde89337",
"title" : "All Live",
"date" : "2012-04-24",
"country" : "CA"
},
"release-group" : {
"id" : "0a5d5f33-8e9d-4fa4-b622-a95e4218a3c4",
"title" : "All Live",
"first-release-date" : "2012-04-24",
"primary-type" : "Album",
"musicbrainz" : "https://musicbrainz.org/release-group/0a5d5f33-8e9d-4fa4-b622-a95e4218a3c4",
"url-rels" : "https://musicbrainz.org/ws/2/release-group/0a5d5f33-8e9d-4fa4-b622-a95e4218a3c4?inc=url-rels&fmt=json",
"coverart" : null
}
}
Now, I would like to update this document, and add new fields into the arrays. The new fields are to be added to certain fields.
Here is the code doing that:
$collection = (new MongoDB\Client)->stream->musicbrainz;
$updateResult = $collection->updateOne(
[
'recording-id' => $out['recording']['id']
],
['$addToSet' => [
'artist' => [
'wikiQiD' => $out['artist']['qid'],
'wiki-extract' => $out['artist']['wiki-extract'],
'wiki-pageid' => $out['artist']['pageid'],
],
'release-group' => [
'wikiQiD' => $out['release-group']['qid'],
'wiki-extract' => $out['release-group']['wiki-extract']
]
]
],
[
'upsert' => true,
'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
]
);
I've noticed there's "$addToSet" and "$push" commands, and could use assistance with what the difference is between these two commands.
If the field is absent in the document to update, $push adds the array
field with the value as its element.
The $addToSet operator adds a value to an array unless the value is
already present, in which case $addToSet does nothing to that array.
I did some googling, and reading of the MongoDB/Client UpdateOne function, but can't seem to find a way to append these fields to the existing arrays.
The error I'm getting is:
Fatal error: Uncaught MongoDB\Driver\Exception\BulkWriteException: The field 'artist' must be an array but is of type object in document {_id: ObjectId('5d1a6aaf5ecc8001ee858f6c')} in ...
I know the following:
It could be my document, as it's not a proper array that Fatal error is complaining about.
It could be my `findOneAndUpdate' formatting, and I'm not doing that correctly.
It could be both and I have it all wrong from the very start.
Any insight or constructive criticism is appreciated, just refrain from flames, pls.
Here's the working code I finally use to get it do what I want it to.
It's a simple matter of just setting your field names and values and mongo will update the found record replacing it with the array sent to it. No need to push or anything.
function firstFindMongoUpdate($out) {
$collection = (new MongoDB\Client)->stream->musicbrainz;
$updateResult = $collection->findOneAndUpdate(
[
'recording-id' => $out['recording']->id
],
['$set' => [
'query' => $out['query'],
'release' => [
'id' => $out['release']->id,
'title' => $out['release']->title,
'date' => $out['release']->date,
'country' => $out['release']->country,
'label' => $out['release']->label,
],
'artist' => [
'id' => $out['artist']->id,
'name' => $out['artist']->name,
'wiki' => $out['artist']->wiki,
],
'recording' => [
'id' => $out['recording']->id,
'title' => $out['recording']->title, // sometimes contains apostophe ie; Bill‘s Love (option ] key)
'score' => $out['recording']->score,
'length' => $out['recording']->length,
'release-count' => count($out['recording']->releases)
],
'release-group' => [
'id' => $out['release-group']['id'],
'title' => $out['release-group']['title'],
'first-release-date'=>$out['release-group']['first-release-date'],
'primary-type' => $out['release-group']['primary-type'],
'musicbrainz' => $out['release-group']['musicbrainz'],
'url-rels' => $out['release-group']['url-rels'],
'coverart' => $out['release-group']['coverart'],
'wiki' => $out['release-group']['wiki']
],
'execution' => [
'artistQuery' => $out['execution']->artistQuery,
'recordingQuery'=> $out['execution']->recordingQuery,
'time' => $out['execution']->time
]
]
],
['upsert' => true,
'projection' =>
[
'_id' => 1,
'query' => 1,
'release' => 1,
'artist' => 1,
'recording' => 1,
'release-group' => 1,
'execution' => 1
],
'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
]
);
I have a CollectionSet<UDT> where UDT contains a CollectionMap<int,boolean>. I have not been able to find any documentation or example of how to define this when creating a new Cassandra\Type\CollectionSet for inserting into the table. There is a great example with a CollectionList (found here) which is like this:
// CollectionSet<UDT>, where UDT contains: Int, Text, Boolean,
// CollectionList<Text>, CollectionList<UDT>
new Cassandra\Type\CollectionSet([
[
'id' => 1,
'name' => 'string',
'active' => true,
'friends' => ['string1', 'string2', 'string3'],
'drinks' => [['qty' => 5, 'brand' => 'Pepsi'], ['qty' => 3, 'brand' => 'Coke']]
],[
'id' => 2,
'name' => 'string',
'active' => false,
'friends' => ['string4', 'string5', 'string6'],
'drinks' => []
]
], [
[
'type' => Cassandra\Type\Base::UDT,
'definition' => [
'id' => Cassandra\Type\Base::INT,
'name' => Cassandra\Type\Base::VARCHAR,
'active' => Cassandra\Type\Base::BOOLEAN,
'friends' => [
'type' => Cassandra\Type\Base::COLLECTION_LIST,
'value' => Cassandra\Type\Base::VARCHAR
],
'drinks' => [
'type' => Cassandra\Type\Base::COLLECTION_LIST,
'value' => [
'type' => Cassandra\Type\Base::UDT,
'typeMap' => [
'qty' => Cassandra\Type\Base::INT,
'brand' => Cassandra\Type\Base::VARCHAR
]
]
]
]
]
]);
I've tried using the above example with several variations to accommodate the CollectionMap but nothing is working. My last attempt was this
new Cassandra\Type\CollectionSet($udt_array, [[
'type'=>Cassandra\Type\Base::UDT,
'definition' => [
'map_name' => [
'type' => Cassandra\Type\Base::COLLECTION_MAP,
'value' => [
Cassandra\Type\Base::INT,
Cassandra\Type\Base::BOOLEAN
]
]
]
]])
which gives the error Caught exception: Since v0.7, collection types should have \"definition\" directive. I've also tried using 'definition' instead of 'value'. I'm running out of ideas, any help would be greatly appreciated.
Use "definition" instead of "value". I tried this before but apparently I was doing something else wrong because this worked.
new Cassandra\Type\CollectionSet($udt_array, [[
'type'=>Cassandra\Type\Base::UDT,
'definition' => [
'map_name' => [
'type' => Cassandra\Type\Base::COLLECTION_MAP,
'definition' => [
Cassandra\Type\Base::INT,
Cassandra\Type\Base::BOOLEAN
]
]
]
]])