I am building an MLM software in PHP, one of its function is to count the downline performance.
It creates an array from parent-child relationships, which can be seen below.
How can I get the performance (array key: points) of my children, and their grandchildren as well through the fifth down level?
Array
(
[children] => Array
(
[0] => Array
(
[id] => 1
[name] => Medvedev
[email] =>
[points] => 7
[children] => Array
(
[0] => Array
(
[id] => 3
[name] => Putin
[email] =>
[points] => 4
[children] => Array
(
[0] => Array
(
[id] => 5
[name] => Nathan
[email] =>
[points] => 3
)
[1] => Array
(
[id] => 7
[name] => George
[email] =>
[points] => 666
)
)
)
[1] => Array
(
[id] => 4
[name] => Lucas
[email] =>
[points] => 43
)
)
)
)
[id] => 27
[name] => Boss
[email] =>
[points] => 99999
)
this should work with unlimited depth starting from a main array like
$array = array(
'children' => array( /* ADD HERE INFINITE COMBINATION OF CHILDREN ARRAY */ ),
'id' => #,
'name' => '',
'email' => '',
'points' => #
);
function recursive_children_points($arr) {
global $hold_points;
if (isset($arr['points'])) {
$hold_points[] = $arr['points'];
}
if (isset($arr['children'])) {
foreach ($arr['children'] as $children => $child) {
recursive_children_points($child);
}
}
return $hold_points;
}
$points = recursive_children_points($array);
print "<pre>";
print_r($points);
/**
// OUTPUT
Array
(
[0] => 99999
[1] => 7
[2] => 4
[3] => 3
[4] => 666
[5] => 43
)
**/
print "<pre>";
In my opinion this calls for recursion because you'll do the same thing over each flat level of the array: add the points.
so you'll need to
loop over each array
add points found
if we find any children, do it again but with children array
while keeping track of levels and jumping out if you reach your limit
With that in mind I thought of the following solution:
<?php
$values = array();
//first
$values[] = array('name'=>'andrej','points'=>1,'children'=>array());
$values[] = array('name'=>'peter','points'=>2,'children'=>array());
$values[] = array('name'=>'mark','points'=>3,'children'=>array());
//second
$values[0]['children'][] = array('name'=>'Sarah','points'=>4,'children'=>array());
$values[2]['children'][] = array('name'=>'Mike','points'=>5,'children'=>array());
//third
$values[0]['children'][0]['children'][] = array('name'=>'Ron','points'=>6,'children'=>array());
//fourth
$values[0]['children'][0]['children'][0]['children'][] = array('name'=>'Ronny','points'=>7,'children'=>array());
//fifth
$values[0]['children'][0]['children'][0]['children'][0]['children'][] = array('name'=>'Marina','points'=>10,'children'=>array());
//sixth
$values[0]['children'][0]['children'][0]['children'][0]['children'][0]['children'][] = array('name'=>'Pjotr','points'=>400,'children'=>array());
function collect_elements($base, $maxLevel,$child='children',$gather='points', &$catch = array(), $level = 0) {
/* I pass $catch by reference so that all recursive calls add to the same array
obviously you could use it straight away but I return the created array as well
because I find it to be cleaner in PHP (by reference is rare and can lead to confusion)
$base = array it works on
$maxLevel = how deep the recursion goes
$child = key of the element where you hold your possible childnodes
$gather = key of the element that has to be collected
*/
$level++;
if($level > $maxLevel) return; // we're too deep, bail out
foreach ($base as $key => $elem) {
// collect the element if available
if(isset($elem[$gather])) $catch[] = $elem[$gather];
/*
does this element's container have children?
[$child] needs to be set, [$child] needs to be an array, [$child] needs to have elements itself
*/
if (isset($elem[$child]) && is_array($elem[$child]) && count($elem[$child])){
// if we can find another array 1 level down, recurse that as well
collect_elements($elem[$child],$maxLevel,$child,$gather, $catch,$level);
}
}
return $catch;
}
print array_sum(collect_elements($values,5)) . PHP_EOL;
collect_elements will collect the element you're interested in (until maximum depth is reached) and append it to a flat array, so that you can act on it upon return. In this case we do an array_sum to get the total of poins collected
Only the first for parameters are interesting:
collect_elements($base, $maxLevel,$child='children',$gather='points'
not optional:
$base is the array to work on
$maxLevel is the maximum depth the function needs to descend into the arrays
optional:
$child defines the key of the element that contains the children of current element (array)
$gather defines the key of the element that contains what we want to gather
The remaining parameters are just ones used for recursion
Related
I have a huge dynamically generated tree. The tree is generated from a flat array based on each elements "parent_id" attribute.
The final result would for example look like this:
Array
(
[0] => Array
(
[id] => 70
[name] => Top Corp
[parent_id] => 0
[children] => Array
(
[0] => Array
(
[id] => 43
[name] => Department
[parent_id] => 70
[children] => Array
(
[0] => Array
(
[id] => 45
[name] => Building
[parent_id] => 43
[children] => Array
(
[0] => Array
(
[id] => 75
[name] => Office
[parent_id] => 45
)
)
)
How do I extract just a part of the array tree? What functions or methods should I look at?
E.g. how do I say another sub-level (potentially 20-30 levels deep) is now the top.
For example, a pseudo function of sliceTree(45) should produce the following result, aka start the tree from id 45
[0] => Array
(
[id] => 45
[name] => Building
[parent_id] => 43
[children] => Array
(
[0] => Array
(
[id] => 75
[name] => Office
[parent_id] => 45
)
)
)
There is no way to know how deep the tree can go so it a solution needs to be recursive.
I have tried looping the array, looking for the starting id, but I am unsure of how to continue the execution after the point has been found.
My proposed solution is as follows
function sliceTree($tree, $id){
$ret = [];
foreach ($tree as $out) {
// if the top level matches
if($out["id"] == $id){
array_push($ret, $out);
}
else {
if(isset($out["children"])){
foreach ($out["children"] as $c) {
if($c["id"] == $id){
array_push($ret, $c);
}
// probably needs to call itself here
}
}
}
}
return $ret;
}
Which works, but only for top-level elements. How can I make recursive and account for the many levels of children?
The sliceTree() function basically looks for a certain id and returns it. Something like this:
function sliceTree($tree, $branchId)
{
// check all branches
foreach ($tree as $branch) {
// have we found the correct branch?
if ($branch['id'] == $branchId) return $branch;
// check the children
if (isset($branch['children'])) {
$slice = sliceTree($branch['children'], $branchId);
if (isset($slice)) return $slice;
}
}
// nothing was found
return null;
}
As you can see, this routine is recursive. Code is untested.
I'm sorry about the mixed metaphors: branches and children, but you started it.
This function is slightly more complex, than I would like it to be, because in your example the children key is absent when there are no children. I would normally expect it to be there and the value to be an empty array.
here is my code
array 1:
Array
(
[0] => Array
(
[id] => 42166
[Company_Website] => http://www.amphenol-highspeed.com/
[company_name] => Amphenol High Speed Interconnect
[city_name] => New York
[country_name] => USA
[comp_img] =>
)
)
array 2:
Array
(
[0] => Array
(
[Product_Name] => CX to CX,Amphenol High Speed Active,Serial Attached SCSI
[company_id] => 42166
)
)
php code:
$total = count($result);
$i=0;
foreach ($result as $key=>$value) {
$i++;
$company_id= implode(",",(array)$value['id']);
if ($i != $total)
echo',';
}
code to fetch array 2:
foreach ($res as $key1=>$value1) {
echo $total;
$event[$value['company_name']] = $value1['Product_Name'];
if($value1['company_id']==$company_id )
{
echo " match";
//$key[['company_name']]= $value1['Product_Name'];
}
else
{
echo "not matched";
}
}
what i need create a new index if company_id is match with id of another array.
that is product_name.
if product name is there just create index otherwise show null.
i want show in key=> value .
output should be like:
Array
(
[0] => Array
(
[id] => 42166
[Company_Website] => http://www.amphenol-highspeed.com/
[company_name] => Amphenol High Speed Interconnect
[city_name] => New York
[country_name] => USA
[comp_img] =>
[Product_Name] => CX to CX,Amphenol High Speed Active,Serial Attached SCSI
)
)
Your all problems with keys in arrays will disappear when you will start using company ids as a keys.
To reindex you arrays, you can use:
$array1 = array_combine(array_column($array1, 'id'), $array1);
$array2 = array_combine(array_column($array2, 'company_id'), $array2);
In the output you will get:
array 1:
Array
(
[42166] => Array
(
[id] => 42166
...
)
)
And array 2 will looks similiar - id as a key.
So accessing to the elements using ids as a keys is a piece of cake right now.
I'm looking for a way to make it so cake returns all database data in the same format/structure... Currently it returns two different types of format depending on the relationship.
If a model 'B' is associated with the current model 'A' being queried it will then place model associations for 'B' underneath it as you can see in [User] below. I want it so that all queries use that structure.
example:
$this->find('all', ....
returns:
Array
(
[0] => Array
(
[UserGroup] => Array
(
[id] => 53
[user_id] => 100003332014851
[media_id] =>
[name] => john
[description] => qwasdfad
)
[User] => Array
(
[id] => 100003332014851
[session_id] => ssm2qbrotmm13ho1ipm8ii2492
[username] =>
[password] => -1
[Planner] => Array
(
)
[Purchase] => Array
(
)
[Listing] => Array
(
)
)
)
I want this to look like:
Array
(
[0] => Array
(
[UserGroup] => Array
(
[id] => 53
[user_id] => 100003332014851
[media_id] =>
[name] => john
[description] => qwasdfad
[User] => Array
(
[id] => 100003332014851
[session_id] => ssm2qbrotmm13ho1ipm8ii2492
[username] =>
[password] => -1
[Planner] => Array
(
)
[Purchase] => Array
(
)
[Listing] => Array
(
)
)
)
)
)
In CakePHP, the find() method return data like your first format. But If you want to format like second one then you have to process it by hand (try to avoid this if possible)
$data = $this->find('all');
$assocs = Set::extract('/User', $data); // extracting all `User` array
foreach($assocs as $key => $assoc) {
unset($data[$key]['User']); // removing the associate `User` from `$data`
$data[$key]['UserGroup']['User'] = $assoc['User']; // adding associate under `UserGroup`
}
ended up doing this... it changes the output to what we need. The top level item does not have a header which is fine I just adjusted our scripts for that... maybe this will help somebody else if they need a custom idea
also no guarantee this covers all possible results but so far it works with all the queries we have.
class AppModel extends Model {
function afterFind($results, $primary) {
//if this is a primary, structure like a secondary so entire site is same format
if ($primary) {
$class = get_class($this);
//simple fix for primary
foreach ($results as $key => $result) {
$result = $this->formatData($result, $class);
$results[$key] = $result;
}
}
return $results;
}
function formatData($result, $class) {
$array = array();
if (isset($result[$class])) {
$array = $result[$class];
unset($result[$class]);
}
$array += $result;
return $array;
}
You can also use contain in this case along with find as UserGroup.User for your desired result
I am having trouble thinking of the logic with the following problem:
I have the following array (has been snipped, as its much larger)
Array
(
[0] => Array
(
[code] => LAD001
[whqc] => GEN
[stocktag] => NONE
[qty] => 54
)
[1] => Array
(
[code] => LAD001
[whqc] => GEN
[stocktag] => NONE
[qty] => 6
)
[2] => Array
(
[code] => LAD004
[whqc] => HOLD
[stocktag] => NONE
[qty] => 6
)
)
I basically need to comebine all the keys in this array so that where the code, whqc and stocktag are the same, add the qty values together. With the example below, I need to end up with this:
Array
(
[0] => Array
(
[code] => LAD001
[whqc] => GEN
[stocktag] => NONE
[qty] => 60
)
[1] => Array
(
[code] => LAD004
[whqc] => HOLD
[stocktag] => NONE
[qty] => 6
)
)
As the first and second keys of the array had the same code, whqc and stocktag, the qty's have been added together into the one key.
Any ideas?
I would suggest combining the group values in to a hash, storing the full array under the hash as a key and if you have a duplicate, add the quantity, then do array_values() to pull the results.
$aggregated = array();
foreach ($records as $cRec) {
// The separator | has been used, if that appears in the tags, change it
$cKey = md5($cRec['code'] . '|' . $cRec['whqc'] . '|' . $cRec['stocktag']);
if (array_key_exists($cKey, $aggregated)) {
$aggregated[$cKey]['qty'] += $cRec['qty'];
} else {
$aggregated[$cKey] = $cRec;
}
}
// Reset the keys to numerics
$aggregated = array_values($aggregated);
I would try something like:
$output = array();
foreach($array as $details){
//make distinct key
$key = $details['code'].'_'.$details['whqc'];
if(!isset($output[$key])){
$output[$key] = $details;
}else{
$output[$key]['qty'] += $details['qty'];
$output[$key]['stocktag'] = $details['stocktag'];
}
}
$output = array_values($output);
print_r($output);
update: Orbling was first ;-)
I have an array of arrays like this:
$cart = Array (
[0] => Array ( [TypeFlag] => S [qty] => 2 [denom] => 50 [totalPrice] => 100 )
[1] => Array ( [TypeFlag] => V [qty] => 1 [denom] => 25 [totalPrice] => 25 )
[2] => Array ( [TypeFlag] => C [qty] => 1 [denom] => 25 [totalPrice] => 25 )
)
Is there any way, short of looping through all of them and checking one at a time, to determine if the TypeFlag value for any of them is S?
Try this:
foreach($cart as $key => $value) {
if ($value['TypeFlag'] == 'S') {
return $key;
}
}
This would return the key of the sub-array that has a TypeFlag value of S. However, this would stop after it finds the first sub-array that matches your search pattern. Not sure what your desired output is tho and how many results are expected. If you can provide more info, I can give you a more accurate example.
Given a function that returns the TypeFlag for each element of your array:
function get_type_flag($item) {
return $item["TypeFlag"];
}
You could apply that function to each element in the array:
$typeflags = array_map("get_type_flag", $cart);
and see if S is in that array:
if (in_array("S", $typeflags)) {
...
}