I have a find method in PHP which calls Mongodb, iterates over the cursor and should fill result array which will return. It looks like:
public function find($collection, $where, $select = [], $options = [])
{
$result = [];
if( $select ) foreach ( $select as $col) $options['projection'][$col] = 1; // Need projection in format: ['id' => 1, 'name' => 1, ...]
/** #var Cursor $cursor */
$cursor = $this->mongoDb->{$collection}->find($where, $options);
$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
//$result = $cursor->toArray(); $result = $cursor;
$i = 0;
foreach ($cursor as $doc) {
if(++$i < 5) Debugger::log($doc);
$doc = $this->parseDocument($doc);
$result[] = $doc;
}
Debugger::log($result);
return $result;
}
The problem is that $result is empty although there are documents in result. It seems $result array is returned before cursor can fill it. It does not make any sense. I can not use $cursor->toArray() cause it has timeout error. How to fix it or how to fix timeout error with toArray()? Collection has above 170 mils. documents, so the timeout is quite a problem.
Related
What I'm trying to do is preserve the order of an array, while determining if each of the numbers match numbers that were pulled from the database. What I have is an array of numbers ($ids), and a non empty array of rows ($rows)
$images = array();
$ids = array(
0 => '41',
1 => '42',
2 => '43',
3 => '44'
);
// database example
$rows = array(
0 => array(
'name' => 'John Doe',
'id' => '42'
),
1 => array(
'name' => 'Jane Doe',
'id' => '43'
),
);
$i = 0;
foreach ($rows as $row) {
// This works, but it doesn't preserve the order of the $ids array
if (in_array($row['id'], $ids[$i])) {
// add $id to a new array
// or use the same $id array
$images[$i]['name'] = $row['name'];
$images[$i]['id'] = $row['id'];
$i++;
}
}
Any help would be appreciated.
Since id is unique in the rows from your database, you can index the $rows array by id to make it easier to look up values there. You can do this with array_column like this:
$rows = array_column($rows, null, 'id');
Or add them to $rows using id as index as you fetch them from the db like this:
while ($row = //whatever query result/prepared statement fetch you're using) {
$rows[$row['id']] = $row;
}
Then, instead of iterating $rows, you can iterate $ids. This will ensure that the resulting $images array will be in the same order as $ids.
foreach ($ids as $id) {
if (isset($rows[$id])) {
$images[] = [
'name' => $rows[$id]['name'],
'id' => $rows[$id]['id']
];
}
}
Another thing that might make this easier (unless you're using the non-matching rows for something else), would be to use the $ids array as a parameter to a WHERE id IN clause in the query that's selecting $rows, so you won't have to do that filtering in PHP. Here's a Q&A that shows how to do that with PDO, for example: PHP - Using PDO with IN clause array.
You could iterate over ids array instead. It will be something like this:
foreach($ids as $id) {
$data = find($id, $rows);
if ($data === null) {
continue;
}
$images[] = [
'name' => $data['name'],
'id' => $data['id']
];
}
function find($id, $rows) {
foreach($rows as $row) {
if($row['id'] == $id) {
return $row;
}
}
return null;
}
Totally agree with #Don't Panic, you should fetch only the data you'll use if possible.
Guess this is a basic question.
How can I make an array similar to this, using a foreach loop?
[
[
'ProductGuid' => '27760c24',
'BaseAmountValue' => 240,
'Quantity' => 1,
'Discount' => 0,
'AccountNumber' => 1000,
'Unit' => 'parts',
],
[
'ProductGuid' => '27760c24',
'BaseAmountValue' => 250,
'Quantity' => 1,
'Discount' => 0,
'AccountNumber' => 1000,
'Unit' => 'parts',
]
],
The following is rejected by the API, i'm trying to connect to:
$arr = array();
foreach($items as $item) {
$arr[]['ProductGuid'] = $item->guid;
$arr[]['BaseAmountValue'] = $item->price;
$arr[]['Quantity'] = $item->qty;
$arr[]['Discount'] = $item->discount;
$arr[]['AccountNumber'] = 1000;
$arr[]['Unit'] = 'parts';
}
Hope one of you will be able to help me :)
An alternative to the other two correct answers but without manually setting the array index or use any temporary variables:
$arr = [];
foreach($items as $item) {
$arr[] = [
'ProductGuid' => $item->guid,
'BaseAmountValue' => $item->price,
'Quantity' => $item->qty,
'Discount' => $item->discount,
'AccountNumber' => 1000,
'Unit' => 'parts',
];
}
Martin explained the actual issue so well so no need to go through it again.
Using the given code, you create new inner rows in your array in each line of that loop. The following code will solve that:
$arr = array();
foreach($items as $item) {
$mappedItem = [];
$mappedItem['ProductGuid'] = $item->guid;
$mappedItem['BaseAmountValue'] = $item->price;
$mappedItem['Quantity'] = $item->qty;
$mappedItem['Discount'] = $item->discount;
$mappedItem['AccountNumber'] = 1000;
$mappedItem['Unit'] = 'parts';
$arr[] = $mappedItem;
}
You're fairly close to one way of doing this...
Explanation:
$arr[]['ProductGuid'] = $item->guid;
^^
\= Next numeric array key.
What this is doing is setting the productGuid key on the next numeric outer array, so in effect what you're actually setting is:
$arr[0]['ProductGuid'] = $item->guid;
$arr[1]['BaseAmountValue'] = $item->price;
$arr[2]['Quantity'] = $item->qty;
$arr[3]['Discount'] = $item->discount;
$arr[4]['AccountNumber'] = 1000;
$arr[5]['Unit'] = 'parts';
Which is clearly not what you want.
One Solution:
Therefore you will have to set the array key value on each iteration of the foreach loop.
One way of doing this is manually setting an iterator integer key value:
$arr = [];
$x = 0;
foreach($items as $item) {
$arr[$x]['ProductGuid'] = $item->guid;
$arr[$x]['BaseAmountValue'] = $item->price;
$arr[$x]['Quantity'] = $item->qty;
$arr[$x]['Discount'] = $item->discount;
$arr[$x]['AccountNumber'] = 1000;
$arr[$x]['Unit'] = 'parts';
$x++; // +1 to value of $x
}
Edit:
Nico's way of doing this is a bit neater and a bit smarter.
Im generating Chart4PHP.
In sample it takes data like this
$p->data = array(array(array("2010/10",-48),array("2011/01",238),array("2011/02",395)));
I have array "rows" constructed of row[date][units].
Im storing it in this way:
$rows = array();
for(...)
{
$row[date] = $mydate;
$row[units]= $myunits;
$rows[]=$row;
}
What I should make additionally to be able to use it as $p->data = $rows;
To add the extra array container, call array() with the rows array as the argument.
$data = array(array('date' => "2010/10", 'units' => -48),
array('date' => "2011/01", 'units' => 238),
array('date' => "2011/02", 'units' => 395));
foreach ($data as $d) {
$mydate = $d['date'];
$myunits = $d['units'];
$rows[] = array($mydate, $myunits);
}
$p->data = array($rows);
I want to get all locations in table travel_location in mysql. This is my code
$select = $this->_db_table->select()->from(travel_location, array('*'));
$result = $this->_db_table->fetchAll($select);
if(count($result) == 0) {
throw new Exception('not found',404);
}
while ($row1 = mysql_fetch_array($result)){
$user_object = new Api_Model_User($row1);
$count = 1;
$json = array($json[$count] = array(
'travel_location_id' => $user_object->travel_location_id,
'city_id' => $user_object->city_id,
'user_id' => $user_object->user_id,
'location_name' => $user_object->location_name,
'description' => $user_object->description,
'longitude' => $user_object->longitude,
'latitude' => $user_object->latitude,
'created_time' => $user_object->created_time,
'updated_time' => $user_object->updated_time));
$count++;
}
It doesn't work. I print $row1 = mysql_fetch_array($result) and it returns false, so I think it's wrong because of this line. How can I fix it?
If you use fetchAll from Zend_Db_Table you get a Zend_Db_Table_Rowset as result.
Try this:
foreach ($result as $row) {
// $row should be a Zend_Db_Table_Row object
// you can cast to array
$rowArray = $row->toArray();
$user_object = new Api_Model_User($rowArray);
}
Read more about here and here
im having a little problem i need some help with.
Im trying to convert a multidimensional array into a flatten array with nested set values right and left like so:
$array = {
'id' => 1
'name' => 'john'
'childs' => array(
array(
'id' => 1
'name' => 'jane'
)
)
}
to
$array = {
array(
'id' => 1,
'name' => 'john'
'left' => '1'
'right' => '4'
),
array(
'id' => 1,
'name' => 'jane'
'left' => '2'
'right' => '3'
)
}
Any help is appreciated!
I asked a very similar question and got a result, so thought I'd send it on for you. I realise this is quite an old topic, but still worth getting an answer. I've included my data, but would be easily adapted for yours.
$JSON = '[{"id":1,"children":[{"id":2,"children":[{"id":3},{"id":4}]},{"id":5}]}]';
$cleanJSON = json_decode($JSON,true);
$a_newTree = array();
function recurseTree($structure,$previousLeft)
{
global $a_newTree; // Get global Variable to store results in.
$indexed = array(); // Bucket of results.
$indexed['id'] = $structure['id']; // Set ID
$indexed['left'] = $previousLeft + 1; // Set Left
$lastRight = $indexed['left'];
$i_count = 0;
if ($structure['children'])
{
foreach ($structure['children'] as $a_child)
{
$lastRight = recurseTree($structure['children'][$i_count],$lastRight);
$i_count++;
}
}
$indexed['right'] = $lastRight + 1; // Set Right
array_push($a_newTree,$indexed); // Push onto stack
return $indexed['right'];
}
recurseTree($cleanJSON[0],0);
print_r($a_newTree);
function restructRecursive($array, $left = 1) {
if (isset($array['childs'])) {
$result = array();
foreach ($array['childs'] as $child) {
$result = array_merge($result, restructRecursive($child, $left+1));
}
unset($array['childs']);
}
$array['left'] = $left;
return array_merge(array($array), $result);
}
$newStruct = restructRecursive($oldStruct);
For anyone coming here and looking for a solution, loneTraceur's solution works fine. However, I needed one in OOP, so here is my version of it.
<?php
class NestedSet
{
protected $tree = [];
public function deconstruct($tree, $left = 0)
{
$this->flattenTree($tree, $left);
return $this->tree;
}
protected function flattenTree($tree, $left)
{
$indexed = [];
$indexed['id'] = $tree['id'];
$indexed['_lft'] = $left + 1;
$right = $indexed['_lft'];
if (isset($tree['children']) && count($tree['children'])) {
foreach ($tree['children'] as $child) {
$right = $this->flattenTree($child, $right);
}
}
$indexed['_rgt'] = $right + 1;
$this->tree[] = $indexed;
return $indexed['_rgt'];
}
}
You would run it like this:
$NestedSet = new NestedSet;
$flat = $NestedSet->deconstruct($tree):
This code is based on the other answer.