MongoDB Group Array Value exists Check - php

I have a Entries looks like
[Title] => test - Title - 33
[readedBy] => Array (0 => 5999,
1 => 85343
)
[clickedBy] => Array (
)
[deletedBy] => Array (
)
And i want to Check in my GROUP reduce function
function (obj, prev) {
obj.readedBy.forEach(function (doc){
if(doc == 800){
prev.status1.push(obj);
}else{
prev.status2.push(obj);
}
});
}
If is there my ID in the Relevan Array to push it to the right Status Array.
Foreach is no solution, because that ID array grows > 100k .
Is there a good way? Or is that solution rubbish ?

So far using mongodb map / reduce from what I've gathered, most of the time when you use map reduce you are going to suffer from an O(n) process, which means you will end up doing .forEach() on your result sets. They get faster as you add nodes. as you shard your collections.

Related

Find the maximum and parent value in an array

I searched many thread but i can't find this solution
I have this Array
Array
( [0] => [1] => Array ( [0] => 2019-01-11T23:30:00CET [1] => -12.6 ) [2] => [3] => Array ( [0] => 2019-01-11T23:20:00CET [1] => -12.5 ) [4] => [5] => Array ( [0] => 2019-01-11T23:10:00CET [1] => -12.6 ) [10] => [11] => Array ( [0] => 2019-01-11T22:40:00CET [1] => -12.4 )
I found the path to have the maximum or minimum value ( Column [1] ) from this Array but i need to find the relative Parent
(example the minimum -12.6 is in the [1][0] as 2019-01-11T22:20:00CET)
of this two values that are show in the first column ( Column[0] )
Thanks
If you use array_column() to extract the second column of your data, then you can use min() or max() with that array to pick which one you want. This code then extracts the ones that match using a foreach() and if to check if it matches (not exactly sure what you want as output, but this should help)...
$source = [["2019-01-11T23:30:00CET", -12.6],
["2019-01-11T23:20:00CET", -12.5],
["2019-01-11T23:10:00CET", -12.6]
];
$extract = min(array_column($source, 1)); // or use max()
$output = [];
foreach ($source as $key => $element) {
if ( $element[1] == $extract ) {
// Matches, so add to output
$output[$key] = $element[0];
}
}
print_r($output);
will give
Array
(
[0] => 2019-01-11T23:30:00CET
[2] => 2019-01-11T23:10:00CET
)
You could use array_filter() to extract the matching rows, but a foreach() is enough for a straightforward thing like this (IMHO).
If there is a possibility of blank values or strings in the value column, this may confuse the min() as it will consider the values and compare them as strings, to ensure they are all compared as numbers you can add...
$values = array_map("floatval", array_column($source, 1));
$extract = min($values); // or use max()
The array_map("floatval",... goes through the list and converts them all to float values.
Also, here's a generalized algorithm-sketch for "finding the max in some array", expressed as pseudo-code:
"Leave quietly" if the array is empty, or throw an exception.
Otherwise, assume that the first element in the array is the biggest one.
Now, loop through the remaining elements, testing if each one is, in fact, bigger than the "biggest one" that you have so far. If so, select it as the "biggest."
When the loop is finished, return your answer.
Now – this is what a geek would call "an O(n) algorithm," which is to say that its execution-time will be "on the order of" the number of elements in the array. Well, if this is a "one-off" requirement, that's fine. Whereas if what you actually want to do is to get "more than one" max-element, sorting the array (once, then holding on to the sorted result ...) becomes significantly better, because the sort is going to be O(log(n)) ... "on the order of some logarithm of the number of elements," ... and the subsequent cost of "popping off" elements from that sorted array becomes non-existent.
There are other ways to do it, of course – trees and such - but I've already blathered-on too long here.

PHP if condition for adding and removing without completely rebuilding

I'm creating a PostgreSQL table column management tool to quickly add, remove and rearrange columns in tables. Thus far it is working great in simple scenarios where I simply append a column to the end or remove an existing column. Each of my if conditions call other functions to append a new column, delete an existing column or to rebuild the table as a whole; these functions work great as far as I can currently test.
How do I create an if condition where I'll call both the append function and the delete function instead of the rebuild function?
I have several arrays to help me determine the logic though I'm having trouble getting it worked out. Here are the arrays of column names and again in this scenario I should not be calling the rebuild function, need to call the append function and the delete function.
In this scenario all I'm going to do is delete the column id_2 and add the column id_4 so there is no need to rearrange the order or columns.
$columns_db - The columns as they exist in the PostgreSQL database.
Array
(
[0] => id
[1] => id_1
[2] => id_2
[3] => id_3
)
$columns_updated - The columns in the database that we're going to keep.
Array
(
[0] => id
[1] => id_2
[2] => id_3
)
$columns_submitted - The final way the columns should appear when all is done,
Array
(
[0] => id
[1] => id_2
[2] => id_3
[3] => id_4
)
$columns_slice
$columns_slice = array_slice($columns_submitted,0,(count($columns_updated) - 1),true);
Array
(
[0] => id
[1] => id_2
)
$columns_remove - The column(s) that will be dropped.
Array
(
[1] => id_1
)
$columns_append - The column(s) that will be created.
Array
(
[id_4] => bigint
)
Perform column deletions first
implode column list after deletions ($before = implode($columns_updated);)
implode column list after additions ($after = implode($columns_submitted))
if (strpos($after, $before) === 0) { /* the additions are at the end */ }
Make sure you use the === operator.
As a one-liner:
$rebuild = (strpos(implode($columns_submitted), implode($columns_updated)) !== 0);
I generally prefer straight forward if / else if conditions though it seemed that it would be simply too wildly convoluted to maintain. #AbraCadaver had the good sense to keep the add/remove detection separate which I adapted (would have up-voted if it wasn't deleted). I've tested about a dozen tricky combinations and haven't been able to find any false-positives. Here is the debug version I used to help visualize the logic:
$rebuild = 0;
foreach ($columns_append as $k1 => $v1)
{
//echo '<div class="clear">k1 = '.$k1.'</div>';
if (in_array($k1,$columns_slice)) {$rebuild = 1; break;}
else
{
$columns_submitted_key = array_search($k1,$columns_submitted);
//echo '<div class="clear">columns_submitted_key: '.$columns_submitted_key.', $columns_updated count = '.count($columns_updated).'</div>';
if ($columns_submitted_key<count($columns_updated)) {$rebuild = 1; break;}//echo '<div class="error">Yeah, rebuild here.</div>';
}
}
if ($rebuild>0) {echo '<div class="clear">1, rebuild.</div>';}
else
{
if (count($columns_append)>0) {echo '<div class="clear">2,append</div>';}
if (count($columns_remove)>0) {echo '<div class="clear">3,delete</div>';}
}

How to best add element to array at arbitrary index in PHP?

How can I write a solution for which the current PHP interpreter (5.4) is smart enough to simply do about 3-5 copies instead of a full on item-by-item array sort?
Note, I know a few methods to insert an element into an indexed array. However this does not satisfy my understanding. For instance in C++, you can do something using std::copy or make a struct or union as a multi-element array cursor.
So I wonder if I play by PHP's rules somehow, what syntax can one use to have, under the hood, something closer to
Copy the [range of elements from some index to the end of A] into temp C
Copy B into A[Index],
Copy C into A[Index+count(B)]
Than this...
$MasterItemList = $Page[$CurrentPage]->GetItems(); /* Returns an array with 512 Items. */
$UpdateList = GetUpdatePage(); /* Returns multi-dimensional array such that:
$result[][0]=an index and
$result[][1]=a list of items */
foreach($UpdateList as $Update)
{ foreach($Update as $cursor => $ItemList)
{
$cursor=$cursor+0; //to int..
$numitems=count($ItemList);
if($ItemList[0]->NewAddition)
{
$BeforeUpdate=array_splice($MasterItemList,0, $cursor, true);
$AfterUpdate=array_splice($MasterItemList, $cursor+$numitems, 0);
$MasterItemList=array_merge($BeforeUpdate,$ItemList,$AfterUpdate);
$Page[$CurrentPage]->OffsetCorrection+=$numitems;
}
else
{
$i=0;
foreach($ItemList as $LineItem)
{
$MasterItemList[$cursor+$i] = $LineItem;
$i++;
}
}
}
}
Forgive me if I've a few errors jotting this down, let me know and I'll correct them.
Namely though, I dont think proper referencing and scope are available to the interpreter for it to be able to do the logic directly using this method. It's already a woefully expensive looking thing.. What can be done to do this 'the right way' for PHP?
Examples:
// An Update List
Array(
[0] => Array(
[0] => 31
[1] => Array(
[1] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
[2] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
[3] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
)
)
)
And MasterItemList is simply an array of these same objects (class Item).
A few things to note:
This data is only accessed in a purely sequential manner anywhere it would matter for this script.
Only the first item in a newly inserted set needs to be checked for update in this part of the script. All items following in the set will be always be new.
Items trailing over 512 are auto-adjusted into the next page load. I can adjust size of pages to trade between array sorting performance & data fetch performance (async buffered).
First of all, PHP arrays are not "arrays" in the data structure sense; they are actually hash tables and doubly linked lists rolled into one. When you are indexing into an array e.g. with $list[$i] $i is hashed to find the corresponding element; it's not simple arithmetic as it is in e.g. C++.
Additionally, since arrays are also linked lists the implementation of array_splice is much more efficient than it might appear, at least if the portion being removed is small enough (hashing the new items is normally fast, and interposing items at a certain place of a linked list is constant time).
Of course this means that PHP arrays consume much more memory than a "pure" array would and they are also slower if all you are intending is index-based access. In those situations the SPL offers SplFixedArray which is an implementation of an array in the data structure sense of the word.
In your particular case, array_splice should be your first option; you can insert an array chunk with just one call:
array_splice($MasterItemList, $cursor, 0, $ItemList);

How to find the index number of an array that contains a certain key/value

(
[1] => Array
(
[rules_properties_id] => 1
[operator] => >=
[value] => 2
[function] => NumOrdersPlaced
[rules_properties_params] => Array
(
[num_days] => 30
[customer_id] => 5
)
)
[2] => Array
(
[rules_properties_id] => 1
[operator] => >=
[value] => 5
[function] => NumOrdersPlaced
[rules_properties_params] => Array
(
[num_days] => 90
[customer_id] => 5
)
)
[3] => Array
(
[rules_properties_id] => 2
[operator] => >
[value] => 365
[function] => CustAcctAge
[rules_properties_params] => Array
(
[customer_id] => 5
)
)
)
That's the print_r of an array that I'm getting back from my database. I need to find the index number of the sub-array that contains the function called NumOrdersPlaced (the expected result would be 2.) Is the only way to do this by looping through the array and subarrays and comparing (as in this answer)? Or is there a more efficient, elegant (i.e. one-liner) function available that I don't know about?
No, there is no one liner for searching multidimensional arrays in PHP, expect you'll write a function for it yourself and use it as one liner :)
Approaches would be:
Change the data structure to somewhat that is good for search operations. eg xml with xpath
When creating the array from your question, create another array which indexes are the function names, and which values are pointers to the sub arrays of the original array
Use the database for that operations. It's optimized for it
...
When searching for the 'right' way you'll have to find a compromise between performance of search, insert, update, remove operations, memory consumption and ease of usage.
As you asked for a PHP solution, here comes an example how I would do it in PHP using and additional index array: (approach 2. from the list above)
// we need two arrays now:
$data = array();
$index = array();
// imagine you loop through database query results
foreach($db_result as $record) {
// create a copy of $record as the address
// of record will contain the last(!) element agter foreach
$item = $record;
// store pointers to that array in data and index
$data []= &$item;
$index[$item->function] = &$item;
}
// here is your one liner
$found = isset($index['NumOrdersPlaced']) ? $index['NumOrdersPlaced'] : NULL;
// another index seach:
$found = isset($index['CustAcctAge']) ? $index['CustAcctAge'] : NULL;
// note there is no additonal loop. The cost is the
// additional memory for $index

Getting a list of children from an array with Parents, without recursion in PHP

I have a situation where I have already obtained and manipulated SQL data into an array and into a tree. I am trying to avoid recursion at all costs because it has bitten me in the behind in the past.
I have an array of elements with Parent_IDs, and I'd like to be able to obtain a list of all of their children and subchildren. It shouldnt be nearly as complex as going the other way (from an array to a nested tree) which I got without issue using references, but for some reason I'm brain-frozen...
Any help appreciated.
I have the data in two possible formats because I have manipulated it already. Whichver is best for input can be used. this is a structure (print_r) of the two arrays I have:
Array
(
[202735] => Array
(
[ID] => 202735
[text] => aaafdf
[Parent] =>
)
[202737] => Array
(
[ID] => 202737
[text] => Filho 2
[Parent] => 202735
)
[202733] => Array
(
[ID] => 202733
[text] => Neto 1
[Parent] => 202731
)
[202739] => Array
(
[ID] => 202739
[text] => Neto 2
[Parent] => 202737
)
)
or
Array
(
[0] => Array
(
[ID] => 202735
[text] => aaafdf
[Parent] =>
[children] => Array
(
[0] => Array
(
[ID] => 202737
[text] => Filho 2
[Parent] => 202735
[children] => Array
(
[0] => Array
(
[ID] => 202739
[text] => Neto 2
[Parent] => 202737
)
)
)
)
)
[1] => Array
(
[ID] => 202733
[text] => Neto 1
[Parent] => 202731
)
)
Desired output in the format:
(first level parent => all children and grandchildren)
array(202731=>array(202735));
array(202735=>array(202737,202739));
or similar... Ideally i'll wrap this in a function like ListChildren($InitialParent), and return all children from such... invoking ListChildren(0) or (null) would list all elements and all subs...
(additional array elements can be ignored for the purpose of this exercise)
OBS: some data in the array is missing... namely the category above 202735, which would be 202731, but thats just because i limited the data I copied in... basically I can have either a flat array with parent ids, or a "tree" array with nested children as the source.
I ended up with this. Thanks for the help, in the end I reverted to a recursive code. I'll monitor it for performance although I suppose just within the PHP layer it wont be an issue. I had a bad experience when I went in for maintenance on a project that had DB calls in a recursive function... as the usage grew the recursvie function usage grew exponentially and the DB calls went to 100s of thousands...
anyways:
function __GetChildrenRec($Lista, $Categoria){
// Return false if $initialParent doesn't exist
if ($Categoria == 0) $Categoria = "";
if (!isset($Lista[$Categoria])) return FALSE;
// Loop data and assign children by reference
foreach ($Lista as $CategAtual) {
if ($CategAtual[Parent] == $Categoria) {
$Filhos[] = $CategAtual[ID];
$Filhos = array_merge((array)$Filhos,(array)self::__GetChildrenRec($Lista, $CategAtual[ID]));
}
}
// Return the data
return is_array($Filhos) ? $Filhos : array();
}
There really is no reason to avoid recursion unless it is causing you a genuine performance problem. Recursion is how most developers looking at problems of this nature would probably attempt to solve them, and using alternative approaches can hurt maintainability because your code is doing something that someone who needs to maintain your code at a later date doesn't expect.
Generally speaking, unrolling recursion means managing a stack. Recursion is a way of getting the language you're using to manage the stack for you, if you don't use recursion, then you'll need to manipulate a stack yourself inside your function. The simplest way of doing this is with array_push and array_pop, functions built into PHP that let you use an array as a stack.
The stack-based approach is rather complex compared to simply using recursion though, and if recursion is giving you problems then manually maintaining a stack certainly will. There are benefits to be sure, but honestly I'd suggest that you try to figure out recursion instead, as it really is easier to deal with, and while it isn't as performant as managing a stack yourself, the performance loss in probably not going to be a bottleneck in your code as the bottlenecks in PHP scripts tend to be where PHP interfaces with the outside world (databases, files, network connections, etc).
Using the first array format:
function list_children ($array, $initialParent) {
// Return false if $initialParent doesn't exist
if (!isset($array[$initialParent])) return FALSE;
// Loop data and assign children by reference
foreach ($array as &$item) {
if (isset($array[$item['parent']])) {
if (!isset($array[$item['parent']]['children'])) $array[$item['parent']]['children'] = array();
$array[$item['parent']]['children'][] = &$item;
}
}
// Return the data
return (isset($array[$initialParent]['children'])) ? $array[$initialParent]['children'] : array();
}
What this does is basically create the second array from the first, but it does it by reference - so the initial parent can still be found by it's ID, and returned. Returns the array of children, an empty array() if there are no children, or FALSE if the $initialParent doesn't exist.

Categories