array iterations too many foreach? any ideas on reducing it - php

protected static function arrayShuffling($itemsArray)
{
$itemSwitching = array();Switching
$shItem= array();
foreach ($itemArray as $i => $myItem) {
if (!in_array($myItem->_id, $itemSwitching)) {
$itemSwitching[$i]['_id'] = $myItem->_id;
$itemSwitching[$i]['poistion'] = $myItem['details']['move_to_poistion'];
}
foreach ($itemSwitching as $t => $pinPrep) {
if ($event->_id == $pinPrep['_id']) {
$shuffeledItem[$itemSwitching[$t]['poistion']] = $myItem;
unset($itemArray[$i]);
}
}
}
foreach($shItem as $key=>$shffuledItem){
array_splice( $itemArray, $key, 0, array($shffuledItem));
}
}
I have this method that takes an array and then look at $myItem['details']['move_to_poistion'] index to see what position that element should be moved to. After that it takes that specific element out of the array and then uses splice to insert it back to the $myItem['details']['move_to_poistion'] position.
I am worried about too many foreach lops and want to know if some how we can shorten this.
thanks

Related

Foreach last item gets methode

Guys I have an array with objects,
I want the last item in the foreach loop do something else then the rest.
How do I archive that?
if(sizeof($testDup) > 3){
} else {
foreach ($testDup as $d) {
}
}
$test array(3)
432 => test_id -> 21
431 => test_id -> 21
435 => test_id -> 21
This will process the array of objects and do something else with the last element:
$data = '';
$arrayWithObjects = array(
(object)array('test1', 'test2'),
(object)array('test1', 'test2'),
(object)array('test1', 'test2'),
);
foreach ($arrayWithObjects as $object) {
// Can't get next in the array, so is last element
if (!next($arrayWithObjects)) {
// Process last element
$data .= $object->{1};
} else {
// Process all other elements
$data .= $object->{0};
}
}
var_dump($data); // "test1test1test2"
you can compare the current one with the end():
class Test {
public function __construct(private string $name) {}
public function read(): string {
return sprintf('%s: hurray', $this->name);
}
public function readLast():string {
return sprintf('%s: am I last?', $this->name);
}
}
$array = [
new Test('first'),
new Test('second'),
new Test('third'),
new Test('fourth'),
];
foreach( $array as $object ){
if($object === end($array)) {
echo $object->readLast().PHP_EOL;
}else{
echo $object->read().PHP_EOL;
}
}
As an alternative to checking if the current item is the last one (which the other answers show), you could use array_slice() to get the start of the array to loop over and then end() to get the last element of the array.
$data = [/*...*/]
foreach ($item as array_splice($data, 0, -1, true) {
$item->foo();
}
if (($item = end($data) !== false) {
$item->bar();
}
In my opinion, this code is easier to read (and metrics like cyclomatic complexity agree) than the nested if $item === end($data) check. If the same is true on your specific case will depend on what, exactly is in the loop and how much of it is different.
In addition, if your array is large, this approach may offer (slightly) better performance (but if your array is large and a small performance difference is important, don't take my word for this - benchmark both solutions with read data).
It's so easy: When the loop is finished, you still got the last element!!
if (!empty($arr)) {
foreach ($arr as $item) {
; // Do something with $item
}
// Here you still got last $item
echo var_export($item, true);
}

How to recursively build a list of Item Substitutions when multiple substitutes exist

In Microsoft Dynamics Nav 2013, there is a feature for specifying Item Substitutions for an item (product); However, you can specify more than one substitution for a single product and technically a substitution can itself have one or more substitutions.
I am trying to build a recursive solution in PHP that allows me to take a known product code, and recursively search for item substitutions to generate a one dimensional array of items. If this were a one to one relationship (parent,child) this would be a trivial task for me, but the fact that there can be multiple childs on any given iteration is kind of blowing my mind.
My question is if anyone knows how to write a recursive method for the situation I've described above? Below I will layout the way the data is structured to give a better understanding of the problem:
$lineItems = array(
'XXX-0',
'XXX-1',
'XXX-3'
);
$substitutionsLookup = array(
0 => array('No_' => 'XXX-1', 'Substitute No_' => 'XXX-2'),
1 => array('No_' => 'XXX-3', 'Substitute No_' => 'XXX-4'),
2 => array('No_' => 'XXX-3', 'Substitute No_' => 'XXX-5'),
3 => array('No_' => 'XXX-5', 'Substitute No_' => 'XXX-6')
);
// Resulting product code substitutions for XXX-0
$result1 = array();
// Resulting product code substitutions for XXX-1
$result2 = array('XXX-2');
// Resulting product code substitutions for XXX-3
$result3 = array('XXX-4', 'XXX-6');
Edit (added my attempt to solve using recursive method):
protected function getSubstitutions($haystack, $needle, &$results = array())
{
if (count($haystack) == 0)
{
return false;
}
$matches = array();
foreach ($haystack as $index => $check)
{
if ($check['No_'] === $needle)
{
$newHaystack = $haystack;
unset($newHaystack[$index]);
$moreMatches = $this->getSubstitutions($newHaystack, $check['Substitute No_'], $results);
if ($moreMatches === false)
{
$matches[] = $check['Substitute No_'];
}
}
}
if (count($matches))
{
foreach($matches as $match)
{
$results[] = $match;
}
}
return $results;
}
Edit (Final code used, derived from accepted answer):
class ItemSubstitutionService implements ServiceLocatorAwareInterface
{
public function getSubstitutions($itemNo, $noInventoryFilter = true, $recursive = true)
{
$substitutions = array();
$directSubs = $this->itemSubstitutionTable->getSubstitutionsByNo($itemNo);
if ($recursive)
{
foreach($directSubs as $sub)
{
$this->getSubstitutionsRecursive($sub, $substitutions);
}
} else {
$substitutions = $directSubs;
}
foreach($substitutions as $index => $sub)
{
$inventory = $this->itemLedgerEntryTable->getQuantityOnHand($sub->getSubstituteNo());
$sub->setInventory($inventory);
if ($noInventoryFilter)
{
if ($inventory == 0)
{
unset($substitutions[$index]);
}
}
}
return $substitutions;
}
private function getSubstitutionsRecursive(ItemSubstitution $sub, &$subs)
{
$directSubs = $this->itemSubstitutionTable->getSubstitutionsByNo($sub->getSubstituteNo());
if (empty($directSubs))
{
$subs[$sub->getSubstituteNo()] = $sub;
}
foreach($directSubs as $curSub)
{
$this->getSubstitutionsRecursive($curSub, $subs);
}
}
}
This code could serve as a solution for your example.
I just presume that you fetch the list of 'direct' items substitutes from your database, so you could replace GetDirectSubstitutes with the code that fetches the substitutes list for the given item (I used you example array as a data source).
Just be careful - this simple implementation doesn't check for cyclical references. If your initial data contains loops, this code will stuck.
function GetDirectSubstitutes($itemNo)
{
global $substitutionsLookup;
$items = array();
foreach ($substitutionsLookup as $itemPair) {
if ($itemPair['No'] == $itemNo) {
array_push($items, $itemPair['SubstNo']);
}
}
return $items;
}
function GetSubstitutesTree($itemNo, &$substitutes)
{
$directSubst = GetDirectSubstitutes($itemNo);
if (!empty($directSubst)) {
$substitutes = array_merge($substitutes, $directSubst);
foreach ($directSubst as $item) {
GetSubstitutesTree($item, $substitutes);
}
}
}

Deleting unknown level of array dynamic(not using eval)

I will have a lot unknown levels of array; I would like to delete the selected ones.
Example:
$data = array(
'level1' => array(
'level2' => array(
'level3' => array(
'level4' => 'the end level at currently example, but not always',
'level4_remain' => 'this data will remain'
),
'remain' => 'this data would not delete in this example too'
)
)
'root' => 'this data would not delete in this example'
);
I have a class called vars; it stores all variables globally that I can fetch at anytime; add at anytime, but now that I'm going to make the delete method, I worry about the multi-level issue:
class vars {
public static $data = array();
public static function get($key){
$key = explode('/', $key);
$v = self::$data;
foreach($key as $k)
{
if(!isset($v[$k]))
{
return null;
}
$v = &$v[$k];
}
return $v;
}
public static function has($key){
.........
}
public static function del($key){
$key = explode('/', $key);
//....Any idea how to do this....//
}
}
and I will use the get method like this:
$blabla = vars::get('level1/level2/level3/level4');
and return correct data, but in the del method, I have no idea what to do:
$deleted = vars::del('level1/level2/level3/level4');
It needs to be finished after deleting the array:
unset($data['level1']['level2']['level3']['level4']);
After doing some research I found this, but this is just for "set" level of array, not automatically set as many level as it can be:
foreach ($data as $k1 => $arr) {
foreach ($arr as $k2 => $arr2) {
foreach ($arr2 as $k3 => $arr3) {
if ($k3 == $key) {
unset($rules[$k1][$k2][$k3]);
}
}
}
}
and I think it can be done like this but is quite ugly:
foreach($data as $k1 => $arr1){
if(is_array($arr1)){
foreach($arr1 as $k2 => $arr2){
//...keep writing the if(is_array()){}else{} and foreach(){} for more level deeper...//
}
}else{
//...the unset should be here...//
}
}
After some research, eval might be harmful, so anyone have any ideas how to do this using any method except eval?
If you don't mind using eval...
public static function del($levels)
{
$lvls = explode('/', $levels);
$count = count($lvls);
$eval = '';
for ($i=0; $i<$count; ++$i) {
// Current Array Key
$key = $lvls[$i];
$eval .= "['$key']";
}
$eval = 'self::data' . $eval;
eval("unset($eval);");
}
However, running untrusted (like User input) through eval can be dangerous.
To avoid eval, you can go the recursive route. The trick is to loop over your indices and pass a subarray reference repeatedly, similar to your get function, just recursively.
public static function del($key){
$key = array_reverse( explode('/', $key) ); // _reverse for _pop later
del_r(self::$data,$key); // start the recursive function
}
private static function del_r(&$array,$keys) { // shouldn't be used from outside
$key = array_pop($keys); // extract next key, this removes it from $keys
if (count($keys)) { // still keys left -> recurse further
del_r($array[$key],$keys); // pass subarray by reference
}
else { // no more keys -> delete the element at the current $key
unset($array[$key]);
}
}
Depending on the number of keys, you can use array_shift() instead of array_pop() and remove the array_reverse().

PHP combinations of array elements

I want to generate all possible combination of array elements to fill a placeholder, the placeholder size could vary.
Let say I have array $a = array(3, 2, 9, 7) and placeholder size is 6. I want to generate something like the following:
3,3,3,3,3,3
2,3,3,3,3,3
2,2,3,3,3,3
...........
...........
7,7,7,7,7,9
7,7,7,7,7,7
However (2,3,3,3,3,3) would be considered the same as (3,2,3,3,3,3) so the later one doesn't count.
Could anyone point me to the right direction? I know there is Math_Combinatorics pear package, but that one is only applicable to placeholder size <= count($a).
Edit
I am thinking that this one is similar to bits string combination though with different number base
I have no PHP source code for you but some sources that might help.
Some C code. Look at 2.1:
http://www.aconnect.de/friends/editions/computer/combinatoricode_g.html
Delphi code: combination without repetition of N elements without use for..to..do
Wiki article here
Well it took quit some time to figure this one out.
So i split the question into multiple parts
1.
I firsrt made an array with all the possible value options.
function create_all_array($placeholder, array $values)
{
if ($placeholder <= 0) {
return [];
}
$stack = [];
$values = array_unique($values);
foreach ($values as $value) {
$stack[] = [
'first' => $value,
'childs' => create_all_array($placeholder - 1, $values)
];
}
return $stack;
}
2.
Then I made a function to stransform this massive amount of data into string (no check for uniques).
function string($values, $prefix = '')
{
$stack = [];
foreach($values as $value) {
$sub_prefix = $prefix . $value['first'];
if (empty($value['childs'])) {
$stack[$sub_prefix] = (int)$sub_prefix;
} else {
$stack = array_merge($stack, string($value['childs'], $sub_prefix));
}
}
return $stack;
}
3.
Then the hard part came. Check for duplicates. This was harder than expected, but found some good anser to it and refactored it for my use.
function has_duplicate($string, $items)
{
$explode = str_split ($string);
foreach($items as $item) {
$item_explode = str_split($item);
sort($explode);
$string = implode('',$explode);
sort($item_explode);
$item = implode($item_explode);
if ($string == $item) {
return true;
}
}
return false;
}
4.
The last step was to combine the intel into a new funciton :P
function unique_string($placeholder, array $values)
{
$stack = string(create_all_array($placeholder, $values));
$check_stack = [];
foreach($stack as $key => $item) {
if (has_duplicate($item, $check_stack)) {
unset($stack[$key]);
}
$check_stack[] = $item;
}
return $stack;
}
Now you can use it simple as followed
unique_string(3 /* amount of dept */, [1,2,3] /* keys */);
Ps the code is based for PHP5.4+, to convert to lower you need to change the [] to array() but I love the new syntax so sorry :P

php. walk up multidimensional array?

suppose I have a multidimensional array structure:
array(parent)
array
array
array(parent)
array(parent)
...some key I'm looking for....
array
array
array
array
array
array
array
I iterate over it recursively to find an array that contains some key I'm looking for - this is no problem.
But then I need to walk up the tree and add additional key to all parents (marked by parent). I can't wrap my head around it. I can easily walk down the tree recursively but I can't figure out how to walk up. Can someone point me to the right direction?
This is an example that I've just wrote just to get the idea.
NOTE that this will break execution on the first occurrence of the matched value.
codepad link
$arr = array(
array('a','b','c','d'),
array(61,62,63,64,65),
array('e','f','g','h'),
array(6,7,8,9),
array(1,2,array(1,2,3,4,5,6,7,8),4,5,6,7,8),
array('i','j','k','l','m'),
);
function array_walkup( $desired_value, array $array, array $keys=array() ) {
if (in_array($desired_value, $array)) {
array_push($keys, array_search($desired_value, $array));
return $keys;
}
foreach($array as $key => $value) {
if (is_array($value)) {
$k = $keys;
$k[] = $key;
if ($find = array_walkup( $desired_value, $value, $k )) {
return $find;
}
}
}
return false;
}
$keys = array_walkup(3, $arr);
echo "3 has been found in \$arr[".implode('][', $keys)."]";
You can use something along these lines in a class:
private $valueFound;
public function recurse($item)
{
if ($item['special_field'] === 'value you are looking for')
{
$this->valueFound = $item['other_field'];
return;
}
if (isset($item['child'])
{
recurse($item['child']);
}
// Use tail-recursion.
// You are at this point past all recursion and are heading back up the tree.
$item['field_to_set'] = $this->valueFound;
}

Categories