PHP, Calculation Key in array - php

i have an array like this
Array
(
[0] => LK10110000
[1] => +
[2] => LK10120000
[3] => -
[4] => LK10130000
)
from that array I want to do a query based on the following sequence of array calculations
expected results :
Value = ResultMysql [LK10110000] + ResultMysql [LK10120000] - ResultMysql [LK10130000]`

From your question and the comments I understand you have a string that contains an expression and you need to run some queries and compute a result base on the expression. And the problem is that you don't know the expression in advance.
I assume your expression contains only addition and subtraction.
If it also contains multiplication or division, parentheses, functions or other operators then the rest of the answer does not apply, it needs more complex code to handle operators precedence, parentheses and function calls.
The idea
Initialize the variable holding the final result with 0.
Split the string into pieces as you already did.
Prepend a + sign to the array of pieces.
Take the first two pieces from the array. The first one is the sign, the second is the variable.
Compose and run the query using the variable as parameter.
Add or subtract the value returned by the query to the final result (check the sign retrieved on step 4 to know if it's add or subtract).
If there still are pieces not processed in the array, repeat from step 4.
The code
The code is less and more clear than the above description:
// This is the input expression
$expression = 'LK10110000; +; LK10120000; -; LK10130000';
// Step 1
$total = 0;
// Step 2
$pieces = explode(';', $pieces);
if (count($pieces) % 2 != 1) {
// The expression is incorrect; handle the situation somehow
//
// A valid expression must contain an odd number of items
// (alternating value and operator, starting and ending with a value)
}
// Extra processing: remove the padding spaces from around the values
// to ensure testing the sign against '-' works correctly
$pieces = array_map('trim', $pieces);
// Step 3
array_unshift($pieces, '+');
// Step 4
do {
$sign = array_unshift($pieces);
$value = array_unshift($pieces);
// Step 5
// ... use $value here to generate and run the query
// ... put the value returned by the query in variable $result
$result = 1; // <-- replace this line
// Step 6
if ($sign === '+') {
$total += $result;
} elseif ($sign === '-') {
$total -= $result;
} else {
// This is an error in the expression; handle it somehow
}
// Step 7
} while (count($pieces));
// The output is in $total
echo($total);
Remarks
If the queries do not return a single numeric value but a set of records ($value is an array of scalar, arrays or objects) then adjust the code on step 6 and use the appropriate merging of $value into $total. Also initialize $total with the correct array of scalars/arrays/objects.
The exact definition of "appropriate merging" depends on the rules of your application. To achieve it you probably must iterate over the elements of $value and for each one, find the corresponding element in $total and update it or insert it if not already present.

Related

Replace repeating value with zero in PHP string

The following is the code
<?php
$id ="202883-202882-202884-0";
$str = implode('-',array_unique(explode('-', $id)));
echo $str;
?>
The result is
202883-202882-202884-0
for $id ="202883-202882-202882-0";, result is 202883-202882-0
I would like to replace the duplicate value with zero, so that the result should be like 202883-202882-0-0, not just remove it.
and for $id ="202883-0-0-0";, result should be 202883-0-0-0. zero should not be replaced, repeating zeros are allowed.
How can I archive that?
More info:
I want to replace every duplicate numbers. Because this is for a product comparison website. There will be only maximum 4 numbers. each will be either a 6 digit number or single digit zero. all zero means no product was selected. one 6 digit number and 3 zero means, one product selected and 3 blank.
Each 6 digit number will collect data from database, I dont want to allow users to enter same number multiple times (will happen only if the number is add with the URL manually.).
Update: I understand that my question was not clear, may be my English is poor.
Here is more explanation, this function is for a smartphone comparison website.
The URL format is sitename.com/compare.html?id=202883-202882-202889-202888.
All three numbers are different smartphones(their database product ID).
I dont want to let users to type in the same product ID like id=202883-202882-202882-202888. It will not display two 202882 results in the website, but it will cause some small issues. The URL will be same without change, but the internal PHP code should consider it as id=202883-202882-202888-0.
The duplicates should be replaced as zero and added to the end.
There will be only 4 numbers separated by "-".
The following examples might clear the cloud!
if pid=202883-202882-202889-202888 the result should be 202883-202882-202889-202888
if pid=202883-202883-202883-202888 the result should be 202888-0-0-0
if pid=202883-202882-202883-202888 the result should be 202883-202882-202888-0
if pid=202882-202882-202882-202882 the result should be 202882-0-0-0
I want to allow only either 6 digit numbers or single digit zero through the string.
if pid=rgfsdg-fgsdfr4354-202883-0 the result should be 202883-0-0-0
if pid=fasdfasd-asdfads-adsfds-dasfad the result should be 0-0-0-0
if pid=4354-45882-445202882-202882 the result should be 202882-0-0-0
It is too complicated for me create, I know there are bright minds out there who can do it much more efficiently than I can.
You can do a array_unique (preserves key), then fill the gaps with 0. Sort by key and you are done :)
+ on arrays will unify the arrays but prioritizes the one on the left.
Code
$input = "0-1-1-3-1-1-3-5-0";
$array = explode('-', $input);
$result = array_unique($array) + array_fill(0, count($array), 0);
ksort($result);
var_dump(implode('-',$result));
Code (v2 - suggested by mickmackusa) - shorter and easier to understand
Fill an array of the size of the input array. And replace by leftover values from array_unique. No ksort needed. 0s will be replaced at the preserved keys of array_unique.
$input = "0-1-1-3-1-1-3-5-0";
$array = explode('-', $input);
$result = array_replace(array_fill(0, count($array), 0), array_unique($array));
var_export($result);
Working example.
Output
string(17) "0-1-0-3-0-0-0-5-0"
Working example.
references
ksort - sort by key
array_fill - generate an array filled with 0 of a certain length
This is another way to do it.
$id = "202883-202882-202882-0-234567-2-2-45435";
From the String you explode the string into an array based on the delimiter which in this case is '-'/
$id_array = explode('-', $id);
Then we can loop through the array and for every unique entry we find, we can store it in another array. Thus we are building an array as we search through the array.
$id_array_temp = [];
// Loop through the array
foreach ($id_array as $value) {
if ( in_array($value, $id_array_temp)) {
// If the entry exists, replace it with a 0
$id_array_temp[] = 0;
} else {
// If the entry does not exist, save the value so we can inspect it on the next loop.
$id_array_temp[] = $value;
}
}
At the end of this operation we will have an array of unique values with any duplicates replaced with a 0.
To recreate the string, we can use implode...
$str = implode('-', $id_array_temp);
echo $str;
Refactoring this, using a ternary to replace the If,else...
$id_array = explode('-', $id);
$id_array_temp = [];
foreach ($id_array as $value) {
$id_array_temp[] = in_array($value, $id_array_temp) ? 0 : $value;
}
$str = implode('-', $id_array_temp);
echo $str;
Output is
202883-202882-0-0-234567-2-0-45435
This appears to be a classic XY Problem.
The essential actions only need to be:
Separate the substrings in the hyphen delimited string.
Validate that the characters in each substring are in the correct format AND are unique to the set.
Only take meaningful action on qualifying value.
You see, there is no benefit to replacing/sanitizing anything when you only really need to validate the input data. Adding zeros to your input just creates more work later.
In short, you should use a direct approach similar to this flow:
if (!empty($_GET['id'])) {
$ids = array_unique(explode('-', $_GET['id']));
foreach ($ids as $id) {
if (ctype_digit($id) && strlen($id) === 6) {
// or: if (preg_match('~^\d{6}$~', $id)) {
takeYourNecessaryAction($id);
}
}
}

Multidimensional sub array subtraction in PHP

Need subarray difference of below array
$arr = array(
array('s'=>'1','e'=>'3'),
array('s'=>'6','e'=>'7'),
array('s'=>'8','e'=>'9'),
array('s'=>'10','e'=>'14'),
array('s'=>'16','e'=>'17'),
)
if(arr[$arr[$i+1][s] - $i][e] <= 1){
//join them
}
else {
//save them as it is
}
Desired result should
$arr = array(
array('s'=>'1','e'=>'3'),
array('s'=>'6','e'=>'14'),
array('s'=>'16','e'=>'17'),
)
No consecutive (next S-E) should be 1
http://codepad.org/V8omMdn6 is where im struck at
See its like
iteration 0
6-3 = 3
so save array('s'=>'1','e'=>'3'),
iteration 1
8-7 = 1
array('s'=>'6','e'=>'9'), => discade in 2 as it
iteration 2
10-9 = 1
array('s'=>'6','e'=>'10'), => discade in 3 as it
iteration 3
10-9 = 1
array('s'=>'6','e'=>'14'),
iteration 4
16-14 = 4
array('s'=>'16','e'=>'17'),
$result = [];
foreach ($arr as $pair) {
if (empty($result) || $pair['s'] - end($result)['e'] > 1) {
$result[] = $pair;
} else {
$result[key($result)]['e'] = $pair['e'];
}
}
You might also use $last as key instead end() & key() for readability.
Using array pointer functions on $result shortens the code but uses some ugly hidden effects. end($result) returns last element of array (using key bracket with function result is possible since php5.3 I guess), but also sets the pointer, so key($result) will return correct key if needed.
While iterating you process last element of result array - this element might not be valid right away, but you don't need to look ahead. There are two scenarios for last element (+initial state condition for empty $result):
invalid: set e value from current item and process further
valid: leave it and push current item into results for further validation (unless that was the last one).
I took a very brief look at your codepen, I think what you want to achieve is to find out if the start time of a new session is within a given period from the end time of the last session, if so you would like to combine those sessions.
I think you confused yourself by trying to subtract start time of new session from end time of last session, it should be the other way round.
The way you worded the question made it even more confusing for people to understand.
If my interpretation of your question is correct, the below code should work with the test case you posted here.
function combineSession($arr){
$arrCount=count($arr)-1;
for ($i=0; $i<$arrCount; $i++){
//if the difference between s and e is less than or equal to one, then there is a consecutive series
if($arr[$i+1]['s']-$arr[$i]['e'] <= 1){
//assign the value of s at the start of a consecutive series to $temp
if (!isset($temp)){
$temp=$arr[$i]['s'];
}
//if consecutive series ends on the last sub_array, write $temp e pair to output
if ($i==$arrCount-1){
$output[]= array('s'=> $temp, 'e' => $arr[$arrCount]['e']);
}
}
//end of a consecutive series, write $temp and e pair to output, unset $temp
else if (isset($temp) && $i<$arrCount-1){
$output[]=array('s'=> $temp, 'e' => $arr[$i]['e']);
unset($temp);
}
//consecutive series ended at the second last sub-array, write $temp and e pair to output and copy key value pair of the last sub-array to output
else if ($i==$arrCount-1){
$output[]=array('s'=> $temp, 'e' => $arr[$i]['e']);
$output[]=$arr[$arrCount];
}
//not in a consecutive series, simply copy s e key value pair to output
else {
$output[]=$arr[$i];
}
}//end of for loop
print_r($output);
}//end of function
else if ($i==$arrCount-1){ $output[]=!isset($temp) ? $arr[$i] : array('s'=> $temp, 'e' => $arr[$i]['e']); $output[]=$arr[$arrCount]; }

php challenge: parse pseudo-regex

I have a challenge that I have not been able to figure out, but it seems like it could be fun and relatively easy for someone who thinks in algorithms...
If my search term has a "?" character in it, it means that it should not care if the preceding character is there (as in regex). But I want my program to print out all the possible results.
A few examples: "tab?le" should print out "table" and "tale". The number of results is always 2 to the power of the number of question marks. As another example: "carn?ati?on" should print out:
caraton
caration
carnaton
carnation
I'm looking for a function that will take in the word with the question marks and output an array with all the results...
Following your example of "carn?ati?on":
You can split the word/string into an array on "?" then the last character of each string in the array will be the optional character:
[0] => carn
[1] => ati
[2] => on
You can then create the two separate possibilities (ie. with and without that last character) for each element in the first array and map these permutations to another array. Note the last element should be ignored for the above transformation since it doesn't apply. I would make it of the form:
[0] => [carn, car]
[1] => [ati, at]
[2] => [on]
Then I would iterate over each element in the sub arrays to compute all the different combinations.
If you get stuck in applying this process just post a comment.
I think a loop like this should work:
$to_process = array("carn?ati?on");
$results = array();
while($item = array_shift($to_process)) {
$pos = strpos($item,"?");
if( $pos === false) {
$results[] = $item;
}
elseif( $pos === 0) {
throw new Exception("A term (".$item.") cannot begin with ?");
}
else {
$to_process[] = substr($item,0,$pos).substr($item,$pos+1);
$to_process[] = substr($item,0,$pos-1).substr($item,$pos+1);
}
}
var_dump($results);

create another multi dimensional array from an array

Suppose i have an array
$x= ('A'=>31, 'B'=>12, 'C'=>13, 'D'=>25, 'E'=>18, 'F'=>10);
I need to generate an array somewhat like this
$newx = (0 => array('A'=>31 , 'B' =>1) , 1 => array('B'=>11 , 'C' =>13 , 'D'=>8) , 2 =>array('D'=>17 , 'E'=>15) , 3=>array('E'=>3,'F'=>10);
Now in this case each value of $newx has to be = 32 and this is how it will work $x[A] = 31 , $x[B] = 12 so first of all we have to make the sum quantity to be 32 keeping the index same for the new array i.e
array(0=>array('A'=>31,'B'=>1) , 1=>array('B'=>11) )
the process should continue for each value of $x.
while I'm pretty sure this is a homework assignment and well, you really should provide code of your own, at least try to, I found the thing amusing so I went ahead and gave it a try. I guess I'll be downvoted for his and I probably do deserve it, but here goes anyway.
What you need to do is:
loop through your array,
determine the elements that give you 32 and then store that result in the final array.
subtract the value of the last element from your result from the corresponding element of your working array
shrink your array next by deleting the first elements until the very first element of the array you're still working with equals the last element your last result returned.
if your last result < 32, quit.
With this in mind, please try to find a solution yourself first and don't just copy-paste the code? :)
<?php
$x = array('A'=>31, 'B'=>12, 'C'=>13, 'D'=>25, 'E'=>18, 'F'=>10);
$result = array();
function calc($toWalk){
// walk through the array until we have gathered enough for 32, return result as an array
$result = array();
foreach($toWalk as $key => $value){
$count = array_sum($result);
if($count >= 32){
// if we have more than 32, subtract the overage from the last array element
$last = array_pop(array_keys($result));
$result[$last] -= ($count - 32);
return $result;
}
$result[$key] = $value;
}
return $result;
}
// logic match first element
$last = 'A';
// loop for as long as we have an array
while(count($x) > 0){
/*
we make sure that the first element matches the last element of the previously found array
so that if the last one went from A -> C we start at C and not at B
*/
$keys = array_keys($x);
if($last == $keys[0]){
// get the sub-array
$partial = calc($x);
// determine the last key used, it's our new starting point
$last = array_pop(array_keys($partial));
$result[] = $partial;
//subtract last (partial) value used from corresponding key in working array
$x[$last] -= $partial[$last];
if(array_sum($partial) < 32) break;
}
/*
reduce the array in size by 1, dropping the first element
should our resulting first element not match the previously returned
$last element then the logic will jump to this place again and
just cut off another element
*/
$x = array_slice($x , 1 );
}
print_r($result);

If is in array?

Really struggling with this one:
I have an existing foreach, containing an if loop looking for specific values. But I also have an array containing values which if found should have the same action taken as the specific values:
Here I loop through and when values are between 5 and 9, I take the value of $datacolvalue and add it to another array as an integer rounded to 2 decimal places.
Otherwise, add it as a string, untouched.
$data_row = array();
$count = 1;
foreach ($row->COLUMN as $datacolvalue){
if($count > 4 && $count < 10)
$data_row[] = round((float)$datacolvalue, 2);
else
$data_row[] = (string)$datacolvalue;
$count++;
}
What I want to do, is is do the same thing $data_row[] = round((float)$datacolvalue, 2); if the value of $count is in a static array named $array_to_round which looks like this (values are different each time the php is run:
array(12,34,56,78);
I though about adding a foreach inside the "else" condition but I cannot get my head around it. Is a for/while/loop the answer?
In a nutshell, for each $datacolvalue, if $count is (> 4) and (< 10) OR is present in the $array_to_round array place in array as int and round, otherwise, place it as a string.
Use in_array to check if the value exists in the other array, then add it as necessary,
I would also store the rounded value if you plan to use it as a check and a setter.

Categories