I'm trying to build an archive-class for my firebird database. And I have the following problem a couple of times already:
I want to construct an array-structure like that:
/**
* #var [] stores the success log of all db operations
*
* $_log = Array(
* (string) [DATA_SOURCE] => Array(
* (int) [0] => Array(
* (string) [id] => (int) 32,
* (string) [action] => (string) "update/insert/delete",
* (string) [state] => (int) 1,
* (string) [message] => (string) "success/error",
* )
* )
* )
*/
private $_log = array();
MY 1. TRY:
// push result to log array
array_push(
$this->_log,
array(
"archive" => array(
"id" => $row["ID"],
"action" => "update",
"state" => $success,
),
)
);
RESULTS IN:
Array(
[0] => Array(
[archive] => Array(
[id] => 32
[action] => update
[state] => 1
)
)
)
That's not exactly what i want. I want the "data-source"-key here "archive" in front of the pushed entry [0].
MY 2nd TRY
array_push(
$this->_log["archive"],
array(
"id" => $row["ID"],
"action" => "update",
"state" => $success,
)
);
RESULTS IN
<br />
<b>Warning</b>: array_push() expects parameter 1 to be array, null given in <b>/Users/rsteinmann/web/intranet/pages/firebird/ArchiveTables.php</b> on line <b>238</b><br />
I'm a bit helpless with this task. I also tried to find anything on google or stackoverflow but there was nothing really useful.
I would be so glad if someone could help me with that!
Thank you,
Raphael
$this->log['archive'][] = array('id' => ...);
This is the sanest way to do it. PHP will create any non-existing keys (like archive) for you. array_push on the other hand is a function call and requires its arguments to already exist, it can't create a non-existing archive key for you. You'd have to do that before you call the function.
array_push is mostly useful if you need to push several arguments at once (array_push($arr, $a, $b, $c)), otherwise $arr[] = $a is the generally preferred and officially recommended syntax.
Related
I have a large multidimensional array that looks like the below.
I want to remove duplicate arrays based on the ID, however, I am struggling to achieve this.
I want the duplicates to work over the entire array, so you can see that ID 1229873 is a duplicate, in the array 2021-07-07 and 2021-07-09, it should therefore be removed from 2021-07-09
How would I achieve this? array_unique has not worked for me.
$data = array (
'2021-07-07' =>
array (
0 =>
array (
'id' => 5435435,
'homeID' => 8754,
'match_url' => '/usa/reading-united-ac-vs-ocean-city-noreasters-fc-h2h-stats#1229873',
'competition_id' => 5808,
'matches_completed_minimum' => 12,
),
1 =>
array (
'id' => 1229873,
'homeID' => 8754,
'match_url' => '/usa/reading-united-ac-vs-ocean-city-noreasters-fc-h2h-stats#1229873',
'competition_id' => 5808,
'matches_completed_minimum' => 12,
),
),
'2021-07-09' =>
array (
0 =>
array (
'id' => 3243234,
'homeID' => 8754,
'match_url' => '/usa/reading-united-ac-vs-ocean-city-noreasters-fc-h2h-stats#1229873',
'competition_id' => 5808,
'matches_completed_minimum' => 12,
),
1 =>
array (
'id' => 1229873,
'homeID' => 8754,
'match_url' => '/usa/reading-united-ac-vs-ocean-city-noreasters-fc-h2h-stats#1229873',
'competition_id' => 5808,
'matches_completed_minimum' => 12,
),
),
);
This is a perfect case for array_uunique()! No wait, scratch that. The PHP devs refused to implement it for the perfectly valid reason of... [shuffles notes] "the function name looks like a typo".
[sets notes on fire]
Anyhow, you just need to iterate over that data, keep track of the IDs you've seen, and remove entries that you've already seen.
$seen = [];
foreach(array_keys($data) as $i) {
foreach(array_keys($data[$i]) as $j) {
$id = $data[$i][$j]['id'];
if( in_array($id, $seen) ) {
unset($data[$i][$j]);
} else {
$seen[] = $id;
}
}
}
I've opted for the foreach(array_keys(...) as $x) approach as avoiding PHP references is always the sane choice.
Run it.
I am Sure That is the way which you want to get the unique array.
$unique = array_map("unserialize", array_unique(array_map("serialize", $data)));
echo "<pre>";
print_r($unique);
echo "</pre>";
I have a config that contains an object with all the relevant information regarding my system. Part of it lists all of the ranks currently in the system:
return (object) array(
//User Ranks
"Ranks" => (object) array(
"Developer" => (object) array(
//Admin, User, Banned, Deactivated, Pending Moderation, Pending Email Activation
"AccountState" => "Admin",
//Page View Permission
"PagePermissions" => array(
"cadet/dashboard",
"admin/dashboard",
"admin/users"
),
"TaskPermissions" => array(
"BasicUserSearch",
"AdvancedUserSearch",
"GetRanks",
"EditUser"
)
),
"Staff" => (object) array(
"AccountState" => "Admin",
//Page View Permission
"PagePermissions" => array(
"cadet/dashboard",
"admin/dashboard",
"admin/users"
),
"TaskPermissions" => array(
"BasicUserSearch",
"AdvancedUserSearch"
)
)
)
);
What I need to do is turn this object into a simple array to return to an AJAX call. The array must look like:
(
"Developer" => array("AccountState" => "Admin),
"Staff" => array("AccountState" => "Admin)
)
I tried doing this with this code:
public function GetRanks(){
$LstRanks = (array)$this->config->Ranks;
for($i = 0; $i < count($LstRanks); $i++){
$LstRanks[$i] = array_column($LstRanks[$i], "AccountState");
}
return json_encode(["data" => 0, "ranks" => $LstRanks]);
}
However, the first line of the function returns null resulting in the loop constantly looping and producing errors. What is the best way to cut the object down like shown?
Error
[30-Apr-2017 18:38:47 UTC] PHP Notice: Undefined offset: 0 in /home/thomassm/public_html/php/lib/UserSystem.php on line 23
[30-Apr-2017 18:38:47 UTC] PHP Warning: array_column() expects parameter 1 to be array, null given in /home/thomassm/public_html/php/lib/UserSystem.php on line 23
public function GetRanks(){
$Ranks = (array) $this->config->Ranks;
$LstRanks = array();
foreach($Ranks as $StrRank => $LstRankInfo){
$LstRanks[$StrRank] = $LstRankInfo->AccountState;
}
return json_encode(["data" => 0, "ranks" => $LstRanks]);
}
This is my solution using the advice from the comments. It seems that I was incorrectly turning the object into an array.
I have a two collections one of all the people I am following and another of what they have been posting on social networking sites like Twitter and Facebook.
The following collection has a subarray of the _id of the feed collection of each user which each status has the word owner and that has the ObjectId that the owner which is the same as the following key. Here is an example.
'_id' => new MongoId("REMOVED"),
'following' =>
array (
'0' => 'ObjectId("53bf464ee7fda8780c8b4568")',
'1' => 'ObjectId("53b00ab5e7fda8304b8b4567")',
),
'owner' => new MongoId("53b9ea3ae7fda8863c8b4123"),
and in the feed you will see that the following.0 status below
array (
'_id' => new MongoId("REMOVED"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
While I can loop through one by one, I can't for some reason do an $or search. I am not quite understanding how I loop through the following array and add it to the search query before I ran the query.
collection = static::db()->feed;
$where=array( '$or' => array(array('owner' => new MongoId($following.0)))));
$feed = $collection->find($where);
return $feed;
now I understand I will somehow have to loop the $where=array( '$or' => array(array('owner' => new MongoId($following.0))))); But I am just not 100% sure how to do this.
Update
As per the answer below I had to edit the array that was returned - now I have only got this working manually and can't seem to get the PHP script to do it.
Answer Returns
Array ( [owner] => Array ( [$in] => Array ( [0] => new MongoId("53bf464ee7fda8780c8b4568") [1] => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
Correct:
Array ( "owner" => Array ( '$in' => Array ( "0" => new MongoId("53bf464ee7fda8780c8b4568"), "1" => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
I am not sure how else to get this to work.
current PHP
$collection = static::db()->following;
$following = $collection->findOne(array ('owner' => new MongoId($_SESSION['user_information'][0]['_id'])));
$follow = $following['following'];
$collection = static::db()->feed;
$where=array("owner" => array( '$in' =>$follow));
print_r($where);
$feed = $collection->find($where);
print_r($feed);
return $feed;
I have fixed a small issue with the collection and now the return array shows
Array ( [owner] => Array ( [$in] => Array ( [0] => MongoId Object ( [$id] => 53bf464ee7fda8780c8b4568 ) [1] => MongoId Object ( [$id] => 53b00ab5e7fda8304b8b4567 ) ) ) )
However, I still can't get it to return the feed like this one:
array (
'_id' => new MongoId("53bf4667e7fda8700e8b4567"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
I am presuming here that this is just a PHPism in the way things are displayed and that your following array is an actual array and not a hash/map, which would generally look like this in a JSON representation:
{
"following": [
ObjectId("53bf464ee7fda8780c8b4568"),
ObjectId("53b00ab5e7fda8304b8b4567"),
],
"owner": ObjectId("53b9ea3ae7fda8863c8b4123"),
}
In which case the "following" is already an actual array, and if you just want to .find() all the "feed" items for the people you are following, then you just pass that to the $in operator for your query selection:
$where = array( "owner" => array( '$in' => $following ) );
$feed = $collection->find($where);
return $feed;
The returned cursor will only contain results from the feed where the "owner" is present in your "following" array from the other collection item.
Watch this code:
$list = array(new MongoId(), new MongoId, new MongoId());
$doc = array( "owner" => array( '$in' => $list ));
echo json_encode( $doc, JSON_PRETTY_PRINT );
Despite how this serializes for JSON by this method the equivalent JSON is:
{
"owner": {
"$in": [
ObjectId("53bf8157c8b5e635068b4567"),
ObjectId("53bf8157c8b5e635068b4568"),
ObjectId("53bf8157c8b5e635068b4569")
]
}
}
That is how the BSON will serialize and is the correct query.
(Answer added on behalf the question author to move it to the answer space).
The issue was fixed when I used the following:
var_dump(iterator_to_array($feed));
is it possible to get the new/updated _id after the query?
example code:
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$mongodb->db->collection->update( $key, $data, array( 'upsert' => true ) );
$key is not holding the new/old _id object and i assume that $data will not either because its just an instruction.
Yes -- It is possible using a single query.
MongoDB includes a findAndModify command that can atomically modify a document and return it (by default it actually returns the document before it's been modified).
The PHP drivers don't include a convenient method for this on the collection class (yet -- check out this bug), but it can still be used (note that my PHP is terrible, so I may very well have made a syntax error in the following snippet):
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$result = $mongodb->db->command( array(
'findAndModify' => 'collection',
'query' => $key,
'update' => $data,
'new' => true, # To get back the document after the upsert
'upsert' => true,
'fields' => array( '_id' => 1 ) # Only return _id field
) );
$id = $result['value']['_id'];
Just in case someone stumbles across this question like I did, Mongo will actually modify the input array when you call MongoCollection->save(); - appending the id to the end.
So, if you call:
$test = array('test'=>'testing');
mongocollection->save($test);
echo $test['_id'];
You will have the mongo id for that object.
I ran into this issue and worked around it by querying back the _id after the upsert. I thought I'd add some of my findings in case they're useful to anyone who comes here searching for info.
When the upsert results in a new document being created in the collection, the returned object contains the _id (here's a print_r of an example):
Array
(
[updatedExisting] => 0
[upserted] => MongoId Object
(
[$id] => 506dc50614c11c6ebdbc39bc
)
[n] => 1
[connectionId] => 275
[fsyncFiles] => 7
[err] =>
[ok] => 1
)
You can get the _id from this:
$id = (string)$obj['upserted'];
However, if the upsert resulted in an existing document being updated then the returned object does not contain _id.
Give this a shot :
function save($data, $id = null) {
$mongo_id = new MongoId($id);
$criteria = array('_id' => $mongo_id);
// Wrap a '$set' around the passed data array for convenience
$update = array('$set' => $data);
$collection->update($criteria, $update, array('upsert' => true));
}
So lets say the passed $id is null, a fresh MongoId is created, otherwise it just converts the existing $id to a MongoId object.
Hope this helps :D
The update method returns an array with the ID of the document UPSERTED:
Array
(
[ok] => 1
[nModified] => 0
[n] => 1
[err] =>
[errmsg] =>
[upserted] => MongoId Object
(
[$id] => 5511da18c8318aa1701881dd
)
[updatedExisting] =>
)
You can also set fsync to true in an update/upsert, to get the _id returned to the object that has been passed to the update.
$save = array ('test' => 'work');
$m->$collection->update(criteria, $save, array('fsync' => true, 'upsert' => true));
echo $save['_id']; //should have your _id of the obj just updated.
I would like to retrieve the first key from this multi-dimensional array.
Array
(
[User] => Array
(
[id] => 2
[firstname] => first
[lastname] => last
[phone] => 123-1456
[email] =>
[website] =>
[group_id] => 1
[company_id] => 1
)
)
This array is stored in $this->data.
Right now I am using key($this->data) which retrieves 'User' as it should but this doesn't feel like the correct way to reach the result.
Are there any other ways to retrieve this result?
Thanks
There are other ways of doing it but nothing as quick and as short as using key(). Every other usage is for getting all keys. For example, all of these will return the first key in an array:
$keys=array_keys($this->data);
echo $keys[0]; //prints first key
foreach ($this->data as $key => $value)
{
echo $key;
break;
}
As you can see both are sloppy.
If you want a oneliner, but you want to protect yourself from accidentally getting the wrong key if the iterator is not on the first element, try this:
reset($this->data);
reset():
reset() rewinds array 's internal
pointer to the first element and
returns the value of the first array
element.
But what you're doing looks fine to me. There is a function that does exactly what you want in one line; what else could you want?
Use this (PHP 5.5+):
echo reset(array_column($this->data, 'id'));
I had a similar problem to solve and was pleased to find this post. However, the solutions provided only works for 2 levels and do not work for a multi-dimensional array with any number of levels. I needed a solution that could work for an array with any dimension and could find the first keys of each level.
After a bit of work I found a solution that may be useful to someone else and therefore I included my solution as part of this post.
Here is a sample start array:
$myArray = array(
'referrer' => array(
'week' => array(
'201901' => array(
'Internal' => array(
'page' => array(
'number' => 201,
'visits' => 5
)
),
'External' => array(
'page' => array(
'number' => 121,
'visits' => 1
)
),
),
'201902' => array(
'Social' => array(
'page' => array(
'number' => 921,
'visits' => 100
)
),
'External' => array(
'page' => array(
'number' => 88,
'visits' => 4
)
),
)
)
)
);
As this function needs to display all the fist keys whatever the dimension of the array, this suggested a recursive function and my function looks like this:
function getFirstKeys($arr){
$keys = '';
reset($arr);
$key = key($arr);
$arr1 = $arr[$key];
if (is_array($arr1)){
$keys .= $key . '|'. getFirstKeys($arr1);
} else {
$keys = $key;
}
return $keys;
}
When the function is called using the code:
$xx = getFirstKeys($myArray);
echo '<h4>Get First Keys</h4>';
echo '<li>The keys are: '.$xx.'</li>';
the output is:
Get First Keys
The keys are: referrer|week|201901|Internal|page|number
I hope this saves someone a bit of time should they encounter a similar problem.