How to simplify and optimize the function? - php

A now write this function for my script. It works well type but a little slows down. Consider the function and if you have options for optimally ask me to help.
Here is my code:
function izada($array) {
foreach ($array as $key => $value) {
if(substr_count($value, "ӣ") == 2) {
$result[] = str_replace("ӣ ", "ӣ, ", $value);
}
if(mb_substr($value, -1) !== "ӣ") {
unset($array[$key]);
}
if(substr_count($value, "ӣ") == 2) {
unset($array[$key]);
}
$array = array_filter(array_unique(array_merge($array, $result)));
}
foreach ($array as $key => $value) {
if(substr_count($value, "ӣ") > 2 || substr_count($value, "ӣ") < 1) {
unset($array[$key]);
}
}
return $array;
}
Input and function call:
$array = array (
"забони тоҷикӣ",
"хуҷандӣ бӯстонӣ",
"Тоҷикистон Ватанам",
"Ғафуровӣ Мичуринӣ Савхозӣ",
"Конверторӣ хуруфҳо"
);
$array = izada($array);
echo"<pre>";
print_r($array);
echo"</pre>";
Result must be:
Array (
[0] => забони тоҷикӣ
[1] => хуҷандӣ, бӯстонӣ
)

Jakub's answer is not optimized and is potentially incorrect according to your posted method.
It allows the possibility of a value with 2 ӣ's but not ending with ӣ to qualify. (If this is acceptable, then you should clarify your question requirements.)
It calls substr_count() 1 to 3 times per iteration (depending on conditional outcomes). The important thing to consider for efficiency is minimizing function calls.
This is a more accurate / efficient process:
Input:
$array=[
"забони тоҷикӣ",
"хуҷандӣ бӯстонӣ",
"Тоҷикистон Ватанам",
"Ғафуровӣ Мичуринӣ Савхозӣ",
"Конверторӣ хуруфҳо"
];
Method: (Demo)
foreach($array as $v){
if(mb_substr($v,-1)=="ӣ"){ // require last char to be ӣ
if(($count=substr_count($v,"ӣ"))==1){
$result[]=$v; // do not replace if only 1 ӣ
}elseif($count==2){
$result[]=str_replace("ӣ ","ӣ, ",$v); // replace qualifying ӣ's if 2 ӣ's
}
}
}
var_export($result);
Output:
array (
0 => 'забони тоҷикӣ',
1 => 'хуҷандӣ, бӯстонӣ',
)
Notice that my method first requires the final character to be ӣ, this offers the quickest return without declaring/overwriting $count for non-qualifying values.
$count is used to cache the result of substr_count() for each iteration. By doing this, the iteration only needs to make the function call once -- improving efficiency.

All the array_merge and array_unique are taking up unnecessary resources. Instead of trying to alter the original array, why not create an output array and fill it with the data you want?
There are also several redundant conditions - you are checking for the same thing several times. From what I understood, this is what you want:
Return all strings where ӣ is present once or twice, either at the end or twice anywhere. If it is present twice, add a coma.
So you could simplify it like
function izada($array) {
$ret = [];
foreach($array as $string){
if (substr_count($string, "ӣ") >= 1 && substr_count($string, "ӣ") <= 2) {
if(substr_count($string, "ӣ") == 2) {
$ret[] = str_replace("ӣ ", "ӣ, ",$string);
}
else if (mb_substr($string, -1) == "ӣ") {
$ret[] = $string;
}
}
}
return $ret;
}

Related

Compare array values and find next value in the array based on custom value (PHP)

I am trying to compare a values in array and select the next value in the array based on a selected value.
For example
array(05:11,05:21,05:24,05:31,05:34,05:41,05:44,05:50,05:54);
and if the the search value is for example 05:34, the one that is returned to be 05:41. If the value is 05:50, 05:54 is returned
I did find something that might help me on this post, but because of the : in my values it will not work.
Any ideas how can I get this to work?
function getClosest($search, $arr) {
$closest = null;
foreach ($arr as $item) {
if ($closest === null || abs($search - $closest) > abs($item - $search)) {
$closest = $item;
}
}
return $closest;
}
UPDATE
Maybe I should somehow convert the values in the array in something more convenient to search within - Just a thinking.
Using the internal pointer array iterator -which should be better from the performance point of view than array_search- you can get the next value like this:
$arr = array('05:11','05:21','05:24','05:31','05:34','05:41','05:44','05:50','05:54');
function getClosest($search, $arr) {
$item = null;
while ($key = key($arr) !== null) {
$current = current($arr);
$item = next($arr);
if (
strtotime($current) < strtotime($search) &&
strtotime($item) >= strtotime($search)
) {
break;
} else if (
strtotime($current) > strtotime($search)
) {
$item = $current;
break;
}
}
return $item;
}
print_r([
getClosest('05:50', $arr),
getClosest('05:34', $arr),
getClosest('05:52', $arr),
getClosest('05:15', $arr),
getClosest('05:10', $arr),
]);
This will output :-
Array (
[0] => 05:50
[1] => 05:34
[2] => 05:54
[3] => 05:21
[4] => 05:11
)
Live example https://3v4l.org/tqHOC
Using array_search() you can find index of array item based of it value. So use it to getting index of searched item.
function getClosest($search, $arr) {
return $arr[array_search($search, $arr)+1];
}
Update:
If search value not exist in array or search value is last item of array function return empty.
function getClosest($search, $arr) {
$result = array_search($search, $arr);
return $result && $result<sizeof($arr)-1 ? $arr[$result+1] : "";
}
Check result in demo
First convert your array value in string because of you use the ':' in the value like
array('05:11','05:21','05:24','05:31','05:34','05:41','05:44','05:50','05:54');
Then use below code to find the next value from the array
function getClosest($search, $arr) {
return $arr[array_search($search,$arr) + 1];
}

How To get sub array keys from array by less than values

I want to get sub array keys from array by less than values.
This is an example:
$arr_less_than = array(55,60,10,70);
$BlackList = array(10,8,15,20);
$MasterArray = array(
10 => array(1 => array(50,20,5,40), 2 => array(70,77,58,10), 3 => array(155,95,110,105), 4 => array(250,215,248,188)),
11 => array(1 => array(5,65,49,100), 2 => array(80,85,60,30), 3 => array(175,85,95,120), 4 => array(235,205,218,284)),
12 => array(1 => array(82,80,55,80), 2 => array(90,90,74,110), 3 => array(180,122,156,222), 4 => array(255,225,233,263)),
13 => array(1 => array(350,360,400,375), 2 => array(95,99,111,75), 3 => array(188,112,66,111), 4 => array(66,69,33,110)),
);
Now I need to get sub array keys from $MasterArray by less than $arr_less_than if the sub array key is not in the array $BlackList.
For the above example, the result must return array(12,13).
Note: I don't want to use a foreach loop
There are 2 solutions here - one for all sub-arrays meet the criteria, and one for any sub-array meets the criteria (which it seems is what the OP had in mind). At the end, there are foreach based solutions for the latter case where ANY sub-array meets the criteria.
If I understand the problem correctly, the goal is to identify rows in MasterArray where all of the sub-arrays have values that are greater than the corresponding value in $arr_less_than.
The OP does not want to use foreach (See the end of the answer for foreach based answers which are much simpler) - which may actually produce a more efficient version because it can avoid unnecessary compares and save some cycles, so here is a heavily annotated version using array functions.
I've excluded the data which can be copied from OP's post:
function getMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
return array_values( // When we're done we just want a straight array of the keys from $MasterArray
array_filter(
array_keys($MasterArray), // Iterate over $MasterArray's keys (Because we need the keys for the BlackList)
function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
// Filter out MasterArray Entries that dont meet the criteria
echo "Evaluate $MasterArray[$v]" . PHP_EOL;
// Remove array entries whose key is in the BlackList
if (in_array($v, $BlackList)) {
echo "\tBlacklisted" . PHP_EOL;
return false;
}
// For each entry in the MasterArray value, add up the number of non-matching entries
$y = array_reduce(
$MasterArray[$v],
function ($c1, $sub) use ($arr_less_than) {
// For each subarray entry in a MasterArray value, reduce the array to a count
// of elements whose value is less than the corresponding value in the $arr_less_than
$s1 = array_reduce(
array_keys($sub),
function ($carry, $key) use ($sub, $arr_less_than) {
if ($sub[$key] <= $arr_less_than[$key]) {
return ++$carry;
}
},
0 // Initial value for the array_reduce method
);
// $s1 will be a count of non-matching values
return $c1 + $s1;
},
0 //Initial value for the array_reduce method
);
echo "\t$y" . PHP_EOL;
// Include the array value in the filter only if there are no non-matching values ($y == 0)
return !$y;
}
)
);
}
print_r(getMatchingRows($arr_less_than, $MasterArray, $BlackList));
The basic idea is to generate a list of keys from the outermost array - so we iterate over them with array_filter. Then we exclude those with a key in the blacklist. Rows that arent in the blacklist, are reduced into an integer by iterating over each sub=arrays values and comparing them positon-wise against $arr_less_than and adding 1 for each value that fails to be greater than the corresponding member in $arr_less_than. Then those values are summed for all of the members in the MasterArray row. If the result is zero, then the row passes. Finally, the ultimate result is passed to array_values to normalize the resulting array.
Note that this requires that all values be compared, even if the first sub-value in the first sub-array fails. For that reason a foreach approach that can escape may be more efficient.
This is essentially the same method without comments and couple of shortcuts:
function getMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
return array_values( // When we're done we just want a straight array of the keys from $MasterArray
array_filter(
array_keys($MasterArray),
function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
return !in_array($v, $BlackList) && !array_reduce(
$MasterArray[$v],
function ($c1, $sub) use ($arr_less_than) {
return $c1 ?: array_reduce(
array_keys($sub),
function ($carry, $key) use ($sub, $arr_less_than) {
return $carry ?: ($sub[$key] <= $arr_less_than[$key] ? 1 : 0);
},
0
);
},
0
);
}
)
);
}
Some of the methods in array_reduce are short-circuited using ?: operator since the actual count is irrelevant. Once the count exceeds zero, the row fails, regardless.
Here is similar code if the criterion is that AT LEAST ONE sub-array has all members greater than the reference array.
function getMatchingRowsAny($arr_less_than, $MasterArray, $BlackList)
{
return array_values( // When we're done we just want a straight array of the keys from $MasterArray
array_filter(
array_keys($MasterArray), // Iterate over $MastrArray's keys (Because we need the keys for theBlackList)
function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
// Filter out MasterArray Entries that dont meet the criteria
echo "Evaluate \MasterArray[$v]" . PHP_EOL;
// Remove array entries whose key is in the BlackList
if (in_array($v, $BlackList)) {
echo "\tBlacklisted" . PHP_EOL;
return false;
}
// For each entry in the MasterArray value, add up the number of non-matching entries
$y = array_reduce(
$MasterArray[$v],
function ($c1, $sub) use ($arr_less_than) {
// For each subarray entry in a MasterArray value, reduce the array to a flag
// indicating if it has whose value is <= the corresponding value in the $arr_less_than
$s1 = array_reduce(
array_keys($sub),
function ($fail, $key) use ($sub, $arr_less_than) {
return $fail || $sub[$key] <= $arr_less_than[$key];
},
false
);
// This could be short-circuited above to avoid an unnecessary array_reduce call
return $c1 || !$s1;
},
false
);
echo "\t$y" . PHP_EOL;
// Include the array value in the filter if there are any matching values
return $y;
}
)
);
}
print_r(getMatchingRowsAny($arr_less_than, $MasterArray, $BlackList));
As an exercise (and because I'm a glutton for punishment) I rendered the same methods using foreach as both a generator and a function returning an array - mostly to illustrate that foreach may be the better choice, and is definitely simpler:
// Implemented as a generator - The associated foreach that uses it follows
function generateMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
foreach ($MasterArray as $k => $v) {
if (in_array($k, $BlackList)) {
continue;
}
foreach ($v as $sub_array) {
$match = true;
foreach ($sub_array as $k1 => $v1) {
if ($v1 <= $arr_less_than[$k1]) {
$match = false;
break;
}
}
if ($match) {
yield $k;
break;
}
}
}
}
foreach (generateMatchingRows($arr_less_than, $MasterArray, $BlackList) as $k) {
echo $k . PHP_EOL; // Or push them onto an array
}
// Implemented as a function returning an array - classical approach - just return an array
function getMatchingRowsForEach($arr_less_than, $MasterArray, $BlackList)
{
$rv = [];
foreach ($MasterArray as $k => $v) {
if (in_array($k, $BlackList)) {
continue;
}
foreach ($v as $sub_array) {
$match = true;
foreach ($sub_array as $k1 => $v1) {
if ($v1 <= $arr_less_than[$k1]) {
$match = false;
break;
}
}
if ($match) {
$rv[] = $k;
break;
}
}
}
return $rv;
}
print_r(getMatchingRowsForEach($arr_less_than, $MasterArray, $BlackList));

Sorting PHP array without ksort

I am trying to manually sort a PHP array without making use of ksort.
This is how my code looks at the moment:
function my_ksort(&$arg){
foreach($arg as $key1 => $value1){
foreach($arg as $key2 => $value2){
if($key1 > $key2){
$aux = $value2;
$arg[$key2] = $value1;
$arg[$key1] = $aux;
}
}
}
}
It doesn't sort, I can't figure out how to make it sort.
You could try this:
function my_ksort(&$arg)
{
$keys=array_keys($arg);
sort($keys);
foreach($keys as $key)
{
$val=$arg[$key];
unset($arg[$key]);
$arg[$key]=$val;
}
}
I'm sorting the keys separately and then deleting the elements one-by-one and appending them to the end, in ascending order.
I'm using another sorting function (sort()), but if you want to eliminate all available sorting functions from your emulation, sort() is much easier to emulate. In fact, #crypticous's algorithm does just that!
This function return array in ASC. Take in consideration that I'm using goto which is supported in (PHP 5 >= 5.3.0)
function ascending_array($array){
if (!is_array($array)){
$array = explode(",", $array);
}
$new = array();
$flag = true;
iter:
$array = array_values($array); // recount array values with new offsets
(isset($min["max"])) ? $min["value"] = $min["max"] : $min["value"] = $array[0];
$min["offset"] = 0;
for ($i=0;$i<count($array);$i++){
if ($array[$i] < $min["value"]){ // redefine min values each time if statement executed
$min["value"] = $array[$i];
$min["offset"] = $i;
}
if ($flag){ // execute only first time
if ($array[$i] > $min["value"]){ // define max value from array
$min["max"] = $array[$i];
}
$flag = false;
}
if ($i === (count($array)-1)){ // last array element
array_push($new,$min["value"]);
unset($array[$min["offset"]]);
}
}
if (count($array)!=0){
goto iter;
}
print_r($new);
}
$arr = array(50,25,98,45);
ascending_array($arr); // 25 45 50 98
PS. When I was studying php, I wrote this function and now remembered that I had it (that's why I really don't remember what I am doing in it, though fact is it's working properly and hopefully there are comments too), hope you'll enjoy :)
DEMO
I was checking some issue related to this post and i wanted to give my insight about it ! here's what i would have done to implement php's sort :
$array_res = array();
$array = array(50,25,98,45);
$i=0;
$temp = $array[0];
$key = array_search($temp, $array);
while ($i<count($array)-1){
$temp = $array[0];
for($n=0;$n<count($array) ;$n++)
{
if($array[$n]< $temp && $array[$n] != -1 )
{
$temp = $array[$n];
}
else{continue;}
}
//get the index for later deletion
$key = array_search($temp, $array);
array_push($array_res, $temp);
/// flag on those which were ordered
$array[$key] =-1;
$i++;
}
// lastly append the highest number
for($n=0;$n<count($array) ;$n++)
{
if ($array[$n] != -1)
array_push($array_res, $array[$n]);
}
// display the results
print_r($array_res);
This code will display : Array
(
[0] => 25
[1] => 45
[2] => 50
[3] => 98
)
Short and sweet
function custom_ksort($arg)
{
$keys = array_keys($arg);
sort($keys);
foreach($keys as $newV)
{
$newArr[$newV] = $arg[$newV];
}
return $newArr;
}
It looks like your issue is that you're changing "temporary" characters $key1 and $key2 but not the actual arrays. You have to change $arg, not just $key1 and $key2.
Try something like:
$arr = Array(3=>"a",7=>"b");
print_r( $arr );
foreach( $arr as $k=>$v ){
unset($arr[$k]);
$arr[$k+1] = $v;
}
print_r($arr);

php remove value from array

Hi I need help in removing values from an array using a recursive function
$array = [0] => testing,testing1
[1] => testing,testing1,testing2
[2] => testing,testing1,testing2,testing3
[3] => testing,testing1,testing2,testing3,tesing4
[4] => testing,testing1,testing2,testing3,tesing4
[5] => testing,testing1,testing2,testing3,tesing4
[6] => testing,testing1,testing2,testing3,tesing4
[7] => testing,testing1,testing2,testing3,tesing4
I need to check the array count, ie if count(array[0]) == count(array[1]),then reutrn array
else unset(array[value]);
From the above array I have to remove array[0],[1],[2] and return rest of the array values.
I've tried the below code
$idx =10;
$separtor =',';
function array_delete($idx, $array,$separtor) {
$finalvalue = array();
for ($i = 0; $i < $idx; $i++) {
$values = explode($separtor, $array[$i]);
$valuesnext = explode($separtor, $array[$i+1]);
if(count($values) != count($valuesnext) )
{
unset($array[$i]);
// reset($array);
// array_delete($idx, $array,$separtor);
if (is_array($array)) $array = array_delete($idx, $array,$separtor);
$finalvalue = $array;
}else
{
}
//echo $i;
}
return $finalvalue;
//(is_array($array)) ? array_values($array) : null;
//array_delete($idx, $array,$separtor);
}
I'm getting Notice: Undefined offset: 0 when trying calling recursive, going to infinite loop
Do you want to keep the sub-arrays that have the most items? Your descriptions appear to say this.
If so, something like the following would suffice.
// Get maximum number of items in the arrays
$max_count = max(array_map('count', $array));
// Keep only those arrays having $max_count items
$filtered = array_filter($array, function ($a) use ($max_count) {
return count($a) === $max_count;
});
Aside: if you need the filtered array to have zero-based keys, call array_values() on it.
See an example running online.
If I understand correctly, you want to filter the array such that any value in the final array is of the same length as the last element in the source array. In order to avoid mutating an array while iterating over it, this technique builds a fresh array with the elements that match your criteria.
$matchLength = count($mainArray[count($mainArray) - 1]);
$resultArray = array();
for($i = 0; $i < count($mainArray); $i++) {
if(count($mainArray[$i]) == $matchLength) {
$resultArray[] = $mainArray[$i];
}
}
If you happen to be using PHP 5.3 or greater, you can do this quicker with closures and array_filter:
$matchLength = count($mainArray[count($mainArray) - 1]);
$resultArray = array_filter($mainArray, function($element){return count($element) == $matchLength});
Double check the code, I haven't been writing PHP lately, so this is just an idea.
According to the description you gave, it could be just made (check the count of the current and the provious one, if they don't match, remove the previous one).
Example/Demo:
unset($prevKey);
$count = array();
foreach (array_keys($array) as $key) {
$count[$key] = count($array[$key]);
if (isset($prevKey) && $count[$prevKey] !== $count[$key]) {
unset($array[$prevKey]);
}
$prevKey = $key;
}
If you need to re-iterate to take removals into account, a little goto can do the job Demo:
start:
######
unset($prevKey);
$count = array();
foreach (array_keys($array) as $key) {
$count[$key] = count($array[$key]);
if (isset($prevKey) && $count[$prevKey] !== $count[$key]) {
unset($array[$prevKey]);
goto start;
###########
}
$prevKey = $key;
}

How to skip the 1st key in an array loop?

I have the following code:
if ($_POST['submit'] == "Next") {
foreach($_POST['info'] as $key => $value) {
echo $value;
}
}
How do I get the foreach function to start from the 2nd key in the array?
For reasonably small arrays, use array_slice to create a second one:
foreach(array_slice($_POST['info'],1) as $key=>$value)
{
echo $value;
}
foreach(array_slice($_POST['info'], 1) as $key=>$value) {
echo $value;
}
Alternatively if you don't want to copy the array you could just do:
$isFirst = true;
foreach($_POST['info'] as $key=>$value) {
if ($isFirst) {
$isFirst = false;
continue;
}
echo $value;
}
Couldn't you just unset the array...
So if I had an array where I didn't want the first instance,
I could just:
unset($array[0]);
and that would remove the instance from the array.
If you were working with a normal array, I'd say to use something like
foreach (array_slice($ome_array, 1) as $k => $v {...
but, since you're looking at a user request, you don't have any real guarantees on the order in which the arguments might be returned - some browser/proxy might change its behavior or you might simply decide to modify your form in the future. Either way, it's in your best interest to ignore the ordering of the array and treat POST values as an unordered hash map, leaving you with two options :
copy the array and unset the key you want to ignore
loop through the whole array and continue when seeing the key you wish to ignore
in loop:
if ($key == 0) //or whatever
continue;
Alternative way is to use array pointers:
reset($_POST['info']); //set pointer to zero
while ($value=next($_POST['info']) //ponter+1, return value
{
echo key($_POST['info']).":".$value."\n";
}
If you're willing to throw the first element away, you can use array_shift(). However, this is slow on a huge array. A faster operation would be
reset($a);
unset(key($a));
On a array filled with 1000 elements the difference is quite minimal.
Test:
<?php
function slice($a)
{
foreach(array_slice($a, 1) as $key)
{
}
return true;
}
function skip($a)
{
$first = false;
foreach($a as $key)
{
if($first)
{
$first = false;
continue;
}
}
return true;
}
$array = array_fill(0, 1000, 'test');
$t1 = time() + microtime(true);
for ($i = 0; $i < 1000; $i++)
{
slice($array);
}
var_dump((time() + microtime(true)) - $t1);
echo '<hr />';
$t2 = time() + microtime(true);
for ($i = 0; $i < 1000; $i++)
{
skip($array);
}
var_dump((time() + microtime(true)) - $t2);
?>
Output:
float(0.23605012893677)
float(0.24102783203125)
Working Code From My Website For Skipping The First Result and Then Continue.
<?php
$counter = 0;
foreach ($categoriest as $category) { if ($counter++ == 0) continue; ?>
It is working on opencart also in tpl file do like this in case you need.
foreach($_POST['info'] as $key=>$value) {
if ($key == 0) { //or what ever the first key you're using is
continue;
} else {
echo $value;
}
}
if you structure your form differently
<input type='text' name='quiz[first]' value=""/>
<input type='text' name='quiz[second]' value=""/>
...then in your PHP
if( isset($_POST['quiz']) AND
is_array($_POST['quiz'])) {
//...and we'll skip $_POST['quiz']['first']
foreach($_POST['quiz'] as $key => $val){
if($key == "first") continue;
print $val;
}
}
...you can now just loop over that particular structure and access rest normally
How about something like this? Read off the first key and value using key() and current(), then array_shift() to dequeue the front element from the array (EDIT: Don't use array_shift(), it renumbers any numerical indices in the array, which you don't always want!).
<?php
$arr = array(
'one' => "ONE!!",
'two' => "TWO!!",
'three' => "TREE",
4 => "Fourth element",
99 => "We skipped a few here.."
) ;
$firstKey = key( $arr ) ;
$firstVal = current( $arr ) ;
echo( "OK, first values are $firstKey, $firstVal" ) ;
####array_shift( $arr ) ; #'dequeue' front element # BAD! renumbers!
unset( $arr[ $firstKey ] ) ; # BETTER!
echo( "Now for the rest of them" ) ;
foreach( $arr as $key=>$val )
{
echo( "$key => $val" ) ;
}
?>

Categories