Filter two arrays of objects - php

I'm trying to use array_filter to filter an array of objects, they share a common value which is warehouse_id.
$warehouse_1 = new warehouse(1, 100, [1,2,3,4]);
$warehouse_2 = new warehouse(2, 1100, [1,2,3,4]);
$warehouse_3 = new warehouse(3, 12000, [1,2,3,4]);
$warehouse_4 = new warehouse(4, 130000, [1,2,3,4]);
$warehouse_5 = new warehouse(5, 1400000, [1,2,3,4]);
$inventory_feed_1 = new inventory_feed(12, 1, "2as21332kjd");
$inventory_feed_2 = new inventory_feed(10, 2, "2123asagfrtsdd");
$inventory_feed_3 = new inventory_feed(11, 3, "2as1231sds2d");
$inventory_feed_4 = new inventory_feed(13, 4, "2as1231sds2d");
$inventory_feed_5 = new inventory_feed(14, 5, "2as1231sds2d");
$ifeeds = ["a" => $inventory_feed_1, "b" => $inventory_feed_2, "c" => $inventory_feed_3];
$warehouses = [$warehouse_1, $warehouse_2, $warehouse_3, $warehouse_4, $warehouse_5];
$warehouses_filtered = array_filter(
$warehouses,
function ($warehouse) use ($ifeeds) {
foreach($ifeeds as $ifeed_id => $ifeed) {
return $ifeed->getWarehouseId() == $warehouse->getId();
});
echo count($warehouses_filtered);
The desired output should be [$warehouse_1, $warehouse_2, $warehouse_3]
but it always returns me the original one (5)

You're returning on the first iteration of the foreach loop, so you're only testing whether the warehouse matches the first feed.
You should return true as soon as you find a match, but not return false until you get all the way through the loop without finding a match.
$warehouses_filtered = array_filter(
$warehouses,
function ($warehouse) use ($ifeeds) {
foreach($ifeeds as $ifeed_id => $ifeed) {
if ($ifeed->getWarehouseId() == $warehouse->getId()) {
return true;
}
}
return false;
});
DEMO

Related

CakePHP 3.8 convert array to Cake\ORM\Entity

I am currently involved in a CakePHP project and do not know how I can pass a modified query/array to a paginator.
Here is my controller:
public function index($fooElement = '')
{
$query = $this->Properties->find()->where(['fooElement' => $fooElement]);
//The fooFunction needs an array cause for an internal call of cakes HASH::NEST function
$data= $this->FooModel->_fooFunction($query->enableHydration(false)->toList();
//Error: Not a paginable object
$data = $this->paginate($data)
$this->set(compact('fooElement', 'data'));
$this->set('_serialize', ['data']);
if (empty($fooElement)) {
$this->render('otherView');
}
}
EDIT: Here is the fooFunction:
public function _fooFunction($data)
{
$out = [];
$cache = [];
$nested = Hash::nest($data, ['idPath' => '{n}.id', 'parentPath' => '{n}.parent_id']);
$out = $this->_setOrderAndLevel($nested);
return $out;
}
protected function _setOrderAndLevel($items, $level = 0, $number = 0)
{
$out = [];
$items = Hash::sort($items, '{n}.orderidx');
foreach ($items as $item) {
$item['level'] = $level;
if (!empty($item['children'])) {
$children = $item['children'];
unset($item['children']);
$out[] = $item;
$out = array_merge($out, $this->_setOrderAndLevel($children, $level + 1));
} else {
$out[] = $item;
}
}
return ($out);
}
The _fooFunction takes the casted database query, makes some adjustments, adds two new properties and returns a nested Array. It maps id with parent_id in order to get children and a level description. The level description will be used for indentations in the view to display a hierarchical order.
IMPORTANT NOTICE: I am already beware of TreeBehavior in CakePHP but the problem is that our database has no left/right fields and I am not able to add them. Within this project I have to choose this way.
However $data contains exactly what I want but I need to transform it into a compatible object for pagination.
EDIT: Thanks to ndm I could build a paginable object with the necessary constraints. The last problem I still have in front of me is to merge all children and possible sub-children. A parent can have nth children and also a children can sometimes have nth sub-children. Therefore I solved this with a recursive call of my _setOrderAndLevel function within the fooFunction.
This is the current structure:
array(
[0] = fooEntity(
id = 1,
orderidx = 1,
parentId = null,
level = 0,
children(
id = 2,
orderidx = 2,
parentId = 1,
level = 1
children(
id = 3,
orderidx = 3,
parentId = 2,
level = 2
........
But it should be this:
array(
[0] = fooEntity(
id = 1,
orderidx = 1,
parentId = null
level = 0
[1] = fooEntity(
id = 2,
orderidx = 2,
parentId = 1,
level = 1
[2] = fooEntity(
id = 3,
orderidx = 3,
parentId = 2,
level = 2
........
I tried to build a second result formatter but it does not work:
...
return $results
->nest('id', 'parent_id', 'children')
->map($decorate);
})
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($data) {
call_user_func_array('array_merge', $data);
});
});
Maybe a "combine->" call could be the solution but I am not sure.
Any help is welcome
Generally if you need to format the results in some way, you should most likely use a result formatter, in order to be able to keep query object intact, and rom looking at the resulting format that your function produces, that is what you should use in this case, a result formatter.
If you need the ordering you could do that on SQL level already, and for nesting the results you could use the result collection's nest() method, ie you could ditch using the Hash class:
$query = $this->Properties
->find()
->where(['fooElement' => $fooElement])
->order(['orderidx' => 'ASC'])
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$fold = function ($rows, $level = 0) use (&$fold) {
$folded = [];
foreach ($rows as $row) {
$row['level'] = $level;
$children = $row['children'] ?: null;
unset($row['children']);
$folded[] = $row;
if ($children) {
$folded = array_merge(
$folded,
$fold($children, $level ++)
);
}
}
return $folded;
};
$nested = $results->nest('id', 'parent_id', 'children');
$folded = $fold($nested);
return collection($folded);
});
Note that you must return an instance of \Cake\Collection\CollectionInterface from the result formatter. The docs say that returning an(y) iterator would be enough, but as soon as there are additional formatters appended that expect a collection, things would break.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Collections > Working with Tree Data

PHP array_merge return null in class

Why this code return null?
I check it but there is no error, And when i am call array merge with 2 default array Like ["x"=>"y","foo"=>"bar"] it work well!
See:
<?php
class ClassName
{
private $dataArray = array();
public function put($arr){
$this->dataArray = array_merge($arr,$this->dataArray);
return $this;
}
public function run(){
echo json_encode($this->dataArray);
}
}
$json = new ClassName();
$json->Test->LastLog = '123456789123456';
$json->Password = 'Mypassword';
$json->Dramatic = 'Cat';
$json->Things = array("HI" => 1, 2, 3);
$json->put($json)->run();
You Passed an object not array try like this code you got result:
$json->put((array)$json)->run();
Output is:
> {"\u0000ClassName\u0000dataArray":["Volvo XC90","BMW
> M4","MaclarenP1"],"Test":"123456789123456","Password":"Mypassword","Dramatic":"Cat","Things":{"HI":1,"0":2,"1":3},"0":"Volvo
> XC90","1":"BMW M4","2":"MaclarenP1"}
EDIT
If you want to pass the $json->Test->LastLog like this means you need to alternate you object declaration like:
$json = new ClassName();
$json->Test = array('LastLog'=>'123456789123456');
$json->Password = 'Mypassword';
$json->Dramatic = 'Cat';
$json->Things = array("HI" => 1, 2, 3);
Because in your put function is array_merge expecting array but you sent a json_object instead of array. (array) is act like json_decode...
Example: Simple Object
$object = new StdClass;
$object->foo = 1;
$object->bar = 2;
var_dump( (array) $object );
Output:
array(2) {
'foo' => int(1)
'bar' => int(2)
}

How can I don't use global variables in this page?

on php document, I made this function.
function getPrices($url) {
global $priceList; // declare global . point of this.
$src = file_get_contents_curl($url);
$dom = new DOMDocument();
$selector = new DOMXPath($dom);
$results = $selector->query('//table/tr/td/span');
foreach($results as $node) {
array_push($priceList, $node->nodeValue);
}
}
and bottom of page, I called it several.
$priceList = array();
getPrices("http://finance.naver.com/item/sise_day.nhn?code=005930");
getPrices("http://finance.naver.com/item/sise_day.nhn?code=005930&page=2");
getPrices("http://finance.naver.com/item/sise_day.nhn?code=005930&page=3");
and display it.
echo $priceList[1];
echo $priceList[2];
echo $priceList[3];
The problem is I'm using CMS kinds of Joomla, Wordpress, and they do not support using global variable So I don't know how to I make this without using global. How can I make it? I need many pages to scrapping, so I'm very afraid. if I scrap just one page,
return in function,
and
$priceList = getPrices("http://finance.naver.com/item/sise_day.nhn?code=$code");
But I don't know many scrapping case. Please help me...
Generally speaking, you shouldn't be using global variables anyways. It's bad practice. Here is one way you can restructure it:
function getPrices($url) {
// this is just a local scoped temp var
$priceList = array();
$src = file_get_contents_curl($url);
$dom = new DOMDocument();
$selector = new DOMXPath($dom);
$results = $selector->query('//table/tr/td/span');
foreach($results as $node) {
array_push($priceList, $node->nodeValue);
}
// return the price list
return $priceList;
}
// here is your real price list
$priceList = array();
// array of urls
$urls = array(
"http://finance.naver.com/item/sise_day.nhn?code=005930",
"http://finance.naver.com/item/sise_day.nhn?code=005930&page=2",
"http://finance.naver.com/item/sise_day.nhn?code=005930&page=3"
// etc..
);
// loop through the urls and assign the results to the price list
foreach ($urls as $url) {
$priceList[] = getPrices($url);
}
Now you have $priceList as an array to do whatever with. Or, if you are looking to immediately output.. you can just skip putting it into $priceList and do your output in the loop above
You could return the partial results from the function and merge them into the complete results array.
<?php
$result = [];
$result = array_merge($result, getSomeValues());
$result = array_merge($result, getSomeValues());
$result = array_merge($result, getSomeValues());
var_export($result);
function getSomeValues() {
static $i = 0;
// returning a partial result of three elements
return [ $i++, $i++, $i++ ];
}
prints
array (
0 => 0,
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
6 => 6,
7 => 7,
8 => 8,
)
You could store the partial results as elements of an array of results.
This way you'd keep some of the information of "what" produced the result.
(You could even use the url as array index)
<?php
$result = [];
$result[] = getSomeValues();
$result[] = getSomeValues();
$result[] = getSomeValues();
// now $result is an array of arrays of (some scalar value)
// so your iteration has to be changed
foreach( $results as $presult ) {
foreach( $presult as $element ) {
..do something with $element
}
}
// or you can "hide" the nesting with
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($result));
foreach($it as $e ) {
echo $e, ', ';
} // see http://docs.php.net/spl
function getSomeValues() {
static $i = 0;
return [ $i++, $i++, $i++ ];
}
The RecursiveIteratorIterator/foreach part prints 0, 1, 2, 3, 4, 5, 6, 7, 8,

change key names in array in php

ok..I'm trying to re-map the keynames of a key-value array in php using a fieldmap array ie.
i want the $outRow array to hold $inRow['name1'] = 10 to $outRow['name_1'] = 10 for a large set of pre-mapped values..
$fieldmap=array("name1"=>"name_1","name2"=>"name_2");
private function mapRow($inRow) {
$outRow = array();
foreach($inRow as $key => $value) {
$outRow[$this->fieldmap[$key]][] = $value;
}
return $outRow;
} // end mapRow
public function getListings($inSql) {
// get data from new table
$result = mysql_query($inSql);
if (!result) {
throw new exception("retsTranslate SQL Error: $inSql");
}
while ($row = mysql_fetch_assoc($result)) {
$outResult[] = $this->mapRow($row);
}
return $outResult;
} // end getListings
this is not working..I'm getting the array but its using $outResult[0][keyname]...I hope this is clear enough :)
$fieldmap=array("name1"=>"name_1","name2"=>"name_2");
private function mapRow($inRow) {
$outRow = array();
foreach($inRow as $key => $value) {
$outRow[$this->fieldmap[$key]][] = $value;
}
return $outRow;
} // end mapRow
while ($row = mysql_fetch_assoc($result)) {
//$outResult[] = $this->mapRow($row);
$outResult[= $this->mapRow($row);
}
I commented your line of code and added new one..it definitely got what you mentioned in question.
If you can structure your arrays to where the keys align with the values (see example below) you can use PHP array_combine(). Just know that you will need to make absolutely sure the array is ordered correctly.
<?php
$fieldmap = array( 'name_1', 'name_2', 'name_3' );
private function mapRow($inRow)
{
$outRow = array_combine( $this->fieldmap, $inRow );
return $outRow;
}
For example, if your array was:
array( 'name1' => 10, 'name2' => 20, 'name3' => 30 );
The new result would be:
array( 'name_1' => 10, 'name_2' => 20, 'name_3' => 30 );
Let me know if this helps.
Try this:
function mapRow($inRow) {
$outRow = array();
foreach($inRow as $key => $value) {
$outRow[preg_replace('/\d/', '_$0', $key,1)] = $value;
}
return $outRow;
}

Evaluating MongoDB-like JSON Queries in PHP

Consider the following (rather complicated) query expressed in this JSON object:
{
"name": "Kindle Fire",
"sale": true,
"price": {
"$gt": 199,
"$lt": 264
},
"price.vat": { // bogus, just to show $a['price.vat'] == $a['price']['vat']
"$lte": 1.2
},
"$or": {
"qty": {
"$gt": 30
},
"eta": {
"$or": {
"$lt": 3,
"$gt": 30
}
}
},
"countriesAvailable": {
"$in": [
"US",
"CA"
]
}
}
Objective
I want to parse that JSON so that it evaluates to the PHP equivalent of (where $a is my target data):
$a['name'] == 'Kindle Fire' &&
$a['sale'] == true &&
(
$a['price'] > 199 && $a['price'] < 264
) &&
$a['price']['vat'] <= 1.2 &&
(
$a['qty'] > 30 ||
(
$a['eta'] < 3 || $a['eta'] > 30
)
) &&
in_array($a['countriesAvailable'], array('US', 'CA'))
I have little experience building expression evaluators. My idea is to traverse the query from the innermost level to the outermost level, calling the corresponding MongoDB operator methods as needed.
Assuming $a matches the query, this would be the evaluation plan:
$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = array();
$query['price']['$gt'] = true;
$query['price']['$lt'] = true;
$query['price']['vat'] = array();
$query['price']['vat']['$lte'] = true;
$query['$or'] = array();
$query['$or']['qty'] = array();
$query['$or']['qty']['$gt'] = false;
$query['$or']['eta'] = array();
$query['$or']['eta']['$or'] = array();
$query['$or']['eta']['$or']['$lt'] = true;
$query['$or']['eta']['$or']['$gt'] = false;
$query['countriesAvailable'] = array();
$query['countriesAvailable']['$in'] = true;
The second step:
$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = array();
$query['price']['$gt'] = true;
$query['price']['$lt'] = true;
$query['price']['vat'] = true;
$query['$or'] = array();
$query['$or']['qty'] false;
$query['$or']['eta'] = array();
$query['$or']['eta']['$or'] true;
$query['countriesAvailable'] = true;
The third step:
$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = true;
$query['$or'] = array();
$query['$or']['qty'] false;
$query['$or']['eta'] true;
$query['countriesAvailable'] = true;
The fourth step:
$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = true;
$query['$or'] = true;
$query['countriesAvailable'] = true;
Since all the values are booleans the evaluation ends returning !in_array(false, $query, true).
If a better approach exists, let me know.
Problem: Accessing Parent Array Keys
I'm stuck trying to get the innermost the elements and the relevant (ignoring operators) array index path, for instance, if I use a RecursiveIteratorIterator I get the correct values for the first iteration:
$nodes = new ArrayIterator($query);
$iterator = new RecursiveArrayIterator($nodes);
$iteratorIterator = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::LEAVES_ONLY);
print_r(iterator_to_array($iteratorIterator));
Array
(
[name] => Kindle Fire HD
[sale] => 1
[$gt] => 30
[$lt] => 3
[$lte] => 1.2
[0] => US
[1] => CA
)
However, it's of little use since I cannot be sure what $a index the keys are referring to, not to mention that the key values are being overwritten by latter entries and the fact that I can't change their values.
I've also tried playing with RecursiveArrayIterator, but without the hasParent() / getParent() methods it doesn't seem to give me much advantage over simply foreach'ing the array.
Any suggestions?
I quickly read your question it sounds like you want to visit leafs and know the key path to them.
so here:
$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
foreach ($ritit as $leafValue) {
$keyPath = array();
foreach (range(0, $ritit->getDepth()) as $depth) {
$keyPath[] = $ritit->getSubIterator($depth)->key();
}
// do something with $keyPath
// or
$hasParent = $ritit->getDepth() > 0;
$parentIter = $ritit->getSubIterator($ritit->getDepth() - 1);
$parentKey = $parentIter->key();
}

Categories