Read string until first comma, then evaluate value of string? - php

I have a string called $DiscountDescription that can sometimes be populated with data like this:
A43544675, A33540055,
Or like this:
A43544675,
Basically, I can have either one value or two values within it, separated by a comma. I really only need the first value. If there's only one value, it always has a comma and a space after it, which is why in my code below I'm removing the comma and space to evaluate the string.
My current code is below. You can see where I'm only accounting for this if there's one value in the string, but not both. So what I'd like to do is find the comma, and grab everything to the left of the comma, and make that equal to $DiscountDescriptionTrimmed.
$DiscountDescription = $_order->getDiscountDescription();
$DiscountDescriptionTrimmed = substr_replace($DiscountDescription ,"",-2);
if ($DiscountDescriptionTrimmed != '') {
if (substr($DiscountDescriptionTrimmed,0,1) === "e" && strlen($DiscountDescriptionTrimmed) === 11){
$_order->setDiscountDescription('Gift Cards ' . $DiscountDescription);
}
elseif (substr($DiscountDescriptionTrimmed,0,1) === "E" && strlen($DiscountDescriptionTrimmed) === 9){
$_order->setDiscountDescription('Gift Cards ' . $DiscountDescription);
}
elseif (substr($DiscountDescriptionTrimmed,0,1) === "A" && strlen($DiscountDescriptionTrimmed) === 9){
$_order->setDiscountDescription('Gift Cards ' . $DiscountDescription);
}
elseif (strlen($DiscountDescriptionTrimmed) === 17 && substr_count($DiscountDescriptionTrimmed,'-') === 2){
$_order->setDiscountDescription('Gift Cards ' . $DiscountDescription);
}
elseif (strlen($DiscountDescriptionTrimmed) === 8 && ctype_digit($DiscountDescriptionTrimmed)){
$_order->setDiscountDescription('Gift Cards ' . $DiscountDescription);
}
}

You can use strtok() to achieve this:
$DiscountDescriptionTrimmed = strtok($DiscountDescription, ', ');
If you ever needed the second value, you can call strtok() again:
$SecondDiscountDescriptionTrimmed = strtok(', ');

The first line gives the length of first value and second line extracts it.
$length = strpos($DiscountDescription, ',') + 1;
$DiscountDescriptionTrimmed = substr($DiscountDescription, 0, $length);

What about this with simple explode() and getting the 0 th index ?
$DiscountDescription1 = 'A43544675, A33540055,';
$DiscountDescription2 = 'A43544675,';
echo explode(',',$DiscountDescription1)[0];
echo "\n";
echo explode(',',$DiscountDescription2)[0];
Demo : https://eval.in/922189

Related

elseif value is less than getting value or greater than

I am trying to show echo 2 but its not working
$zipcode1 = 07300-011;
$zipcode= str_replace("-","",$zipcode1);
$zipcode = 07300011;
if ($zipcode >= 20000000 && $zipcode <= 26600999) {
echo '1';
}
elseif ($zipcode >= 07000001 && $zipcode <= 07399999) {
echo '2';
}
else {
echo 'no value';
}
Please let me know where i am doing wrong. Thank you for the help
Result = 2
You need to compare string with string if the leading 0 of the zipcode is important:
$zipcode1 = "07300-011";
$zipcode= str_replace("-","",$zipcode1);
$zipcode = "07300011";
if ($zipcode >= "20000000" && $zipcode <= "26600999") {
echo '1';
} elseif ($zipcode >= "07000001" && $zipcode <= "07399999") {
echo '2';
} else {
echo 'no value';
}
You made two mistakes.
One is that the value assigned to $zipcode1 is not a string, but rather the result of an arithmetic operation. I'm saying $zipcode1 is not "07300-011", but rather 07300-011, which is equal to the octal number 7300 (3776 in base 10) minus octal 11 (9 in base 10), i.e 3776 - 9 which is 3767.
The second is that you're trying to do a numeric comparison using strings. "20" > "1000" is not the same as 20 > 1000. The first would resolve to true, whereas the second would give false (which is likely what you want).
To fix this, you have to first convert both of them to numbers. You can either cast them:
((int) $zipcode1) > (int) $zipcode2
or you can use the + sign instead:
(+$zipcode1) > (+$zipcode2)
In both cases, you need to first remove whitespaces and every other non-numeric character from the zipcode.
$zipcode = str_replace([' ', '-'], '', $zipcode);
Read the following topics in the php docs for more info:
Numeric Strings
Comparison Operators

Without Regex: String Between Quotes?

I'm creating a word-replacement script. I've run into a roadblock with ignoring strings between quotes and haven't been able to find a decent solution here that didn't involve Regex.
I have a working snippet that cycles through every character in the string and figures out whether the most recent quotation was an opening or closing quote (Whether single or double) and ignores escaped quotes. The problem is that in order for it to provide a 100% accurate experience, it has to run every time the string changes (Because of how it works, it could change well over 60K times across a single function), and due to string length potential, the code takes too long even on a fairly short script.
Is there a fast way to figure out whether a string is between open and close quotes (Single and double)? Ignoring escaped " and '. Or, do you have suggestions on how to optimize the snippet to make it run significantly faster? Removing this function, the process runs at almost the preferred speed (Instant).
As an exercise, consider copying and pasting the snippet into the script with a variable containing text. For example $thisIsAQuote = "This is a quote."; And, from that point, everything should replace correctly, except $thisIsAQuote should retain its exact text.
But here's the issue: Other solutions I've found will treat everything between "This is a quote." and ... $this->formatted[$i - 1] != " ... as if it's still between quotes. Because as far as those solutions are concerned, the last quote in "This is a quote." and the first quote in the if-check are open and close quotes. Another obvious issue is that some strings contain words with apostrophes. Apostrophes shouldn't be treated as single-quotes, but in all solutions I've found, they are.
In other words, they're "unaware" solutions.
$quoteClosed = true;
$singleQuoteClosed = true;
$codeLength = mb_strlen($this->formatted);
if ($codeLength == false)
return;
for ($i = 0; $i < $codeLength; $i++)
{
if ((!$quoteClosed || !$singleQuoteClosed) && ($this->formatted[$i] == '"' || $this->formatted[$i] == "'"))
{
if (!$quoteClosed && $this->formatted[$i - 1] != "\\")
$quoteClosed = true;
else if (!$singleQuoteClosed && $this->formatted[$i - 1] != "\\")
$singleQuoteClosed = true;
}
else if ($this->formatted[$i] == '"' && ($i <= 0 || $this->formatted[$i - 1] != "\\"))
{
if ($quoteClosed && $singleQuoteClosed)
$quoteClosed = false;
}
else if ($this->formatted[$i] == "'" && ($i <= 0 || $this->formatted[$i - 1] != "\\"))
{
if ($singleQuoteClosed && $quoteClosed)
$singleQuoteClosed = false;
}
if ($quoteClosed && $singleQuoteClosed)
$this->quoted[$i] = 0;
else
$this->quoted[$i] = 1;
}
If there isn't a way to make the above more efficient, is there a non-Regex way to quickly replace all substrings in an array with substrings in a second array without missing any across an entire string?
substr_replace and str_replace only seem to replace "some" pieces of the overall string, which is why the number of iterations are in place. It cycles through a while loop until either strpos deems a string nonexistent (Which it never seems to do ... I may be using it wrong), or it cycles through 10K times, whichever occurs first.
Running the above snippet -once- per round would solve the speed issue, but that leaves the "full-replacement" issue and, of course, staying aware that it should avoid replacing anything within quotes.
for ($a = 0; $a < count($this->keys); $a++)
{
$escape = 0;
if ($a > count($this->keys) - 5)
$this->formatted = $this->decodeHTML($this->formatted);
while (strpos($this->formatted, $this->keys[$a]) !== false)
{
$valid = strpos($this->formatted, $this->keys[$a]);
if ($valid === false || $this->quoted[$valid] === 1)
break;
$this->formatted = substr_replace($this->formatted, $this->answers[$a], $valid, mb_strlen($this->keys[$a]));
$this->initializeQuoted();
$escape++;
if ($escape >= 10000)
break;
}
if ($a > count($this->keys) - 5)
$this->formatted = html_entity_decode($this->formatted);
}
$this->quoted = array();
$this->initializeQuoted();
return $this->formatted;
'keys' and 'answers' are arrays containing words of various lengths. 'formatted' is the new string with the changed information. 'initializeQuoted' is the above snippet. I use htmlentities and html_entity_decode to help get rid of whitespaces with key/answer replacements.
Ignore the magic numbers (5s and 10K).
If I understand you correctly then you can do this:
$replacements = [
"test" => "banana",
"Test" => "Banana"
];
$brackets = [[0]];
$lastOpenedQuote = null;
for ($i = 0;$i < strlen($string);$i++) {
if ($string[$i] == "\\") { $i++; continue; } //Skip escaped chars
if ($string[$i] == $lastOpenedQuote) {
$lastOpenedQuote = null;
$brackets[count($brackets)-1][] = $i;
$brackets[] = [ $i+1 ];
} elseif ($lastOpenedQuote == null && ($string[$i] == "\"" || $string[$i] == "'")) {
$lastOpenedQuote = $string[$i];
$brackets[count($brackets)-1][] = $i-1;
$brackets[] = [ $i ];
}
}
$brackets[count($brackets)-1][] = strlen($string)-1;
$prev = 0;
$bits = [];
foreach ($brackets as $index => $pair) {
$bits[$index] = substr($string,$pair[0],$pair[1]-$pair[0]+1);
if ($bits[$index][0] != "\"" && $bits[$index][0] != "'") {
$bits[$index] = str_replace(array_keys($replacements),array_values($replacements), $bits[$index]);
}
}
Check it out at: http://sandbox.onlinephpfunctions.com/code/0453cb7941f1dcad636043fceff30dc0965541ee
Now if performance is still an issue keep in mind this goes through each string character 1 time and does the minimum number of checks it needs each time so it will be really hard to reduce it more. Perhaps you should revise your approach from the bottom up if you need something faster like e.g. doing some of the splitting on the client-side progressively instead of on the whole string on the serverside.
I was just working on this. Hope this gives you some additional ideas.
MATCH: ["]([\w\s\(\)\.\d\_\-\[\]\{\}]+|\s*)["]
REPLACE: ""
<?xml version="1.0" encoding="UTF-8"?>
<NotepadPlus>
<ScintillaContextMenu>
<!--
NOTES: BLAH
-->
[WEBSITE]
https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/installer/nativeLang/english.xml
-->
<Item MenuId="Tools" MenuItemName="Generate..."/>
<Item MenuEntryName="Edit" FolderName="Remove Lines" MenuItemName="Remove Empty Lines" ItemNameAs="Empty Lines"/>
<Item MenuEntryName="Plugins" FolderName="Remove Lines" MenuItemName="Remove duplicate lines" ItemNameAs="Duplicate Lines (Plugin)"/>
<Item MenuEntryName="Edit" FolderName="Remove Lines" MenuItemName="Remove Consecutive Duplicate Lines" ItemNameAs="Duplicate Lines"/>
<Item MenuEntryName="Search" FolderName="Add Style Tokens" MenuItemName="Using 1st Style" ItemNameAs="1"/>
<Item id="45003" Foldername="Convert" ItemNameAs="Macintosh (CR)"/>
<Item id="0" FolderName="XML Tools"/>
<Item MenuEntryName="Plugins" FolderName="XML Tools" MenuItemName="Options..." ItemNameAs="Options"/>
</ScintillaContextMenu>
</NotepadPlus>
Let me know if you come up with anything else.

Why does this for-loop comparison fail after auto-incrementing on letters

I just gave this answer : https://stackoverflow.com/a/25688064/2627459 in order to loop over letters combination, with the following code :
for ($letter = 'a'; ; ++$letter) {
echo $letter . '<br>';
if ($letter == 'zz') break;
}
Which is just working fine.
I then tried to move the break into the for loop comparison, feeling that it would be better :
for ($letter = 'a'; $letter < 'zz'; ++$letter) {
echo $letter . '<br>';
}
But of course, the last value (zz) wasn't showing, so I tried :
for ($letter = 'a'; $letter < 'aaa'; ++$letter) {
echo $letter . '<br>';
}
And I don't know why, but it's giving me the following output :
a
So I tried several entries, and the (weird) results are :
Entry : $letter < 'yz' -
Output : Up to y only
Entry : $letter < 'zzz' -
Output : Up to zzy
I don't get why it works when the chain starts with z, but it fails in any other case (letter).
Morover, in the case of $letter < 'aaa', it displays a, but not the next one. At worst, I would have expected it to fail with a < 'aaa' and so display nothing. But no.
So, where does this behavior come from, am I missing something on how PHP compare these values ?
(I'm not looking for a workaround, but for an explanation. By the way, if any explanation comes with a working code, it's perfect !)
Comparison is alphabetic:
$letter < 'yz'
When you get to y, you increment again and get z..... alphabetically, z is greater than yz
If you use
$letter != 'yz'
for your comparison instead, it will give you up to yy
So
for ($letter = 'a'; $letter !== 'aaa'; ++$letter) {
echo $letter . '<br>';
}
will give from a, through z, aa, ab.... az, ba.... through to zz.
See also this answer and related comments
EDIT
Personally I like incrementing the endpoint, so
$start = 'A';
$end = 'XFD';
$end++;
for ($char = $start; $char !== $end; ++$char) {
echo $char, PHP_EOL;
}

PHP Number Format - Aligning Numbers with Brackets

I'm using the following function to turn my numbers into 2 decimals with negatives in parenthesis.This almost works
However as I have them right aligned they don't quite line up as i'd like.
The ) sits above the 3. It would be better if the 4 and 3 aligned above each other.
(11,870.74)
2,806.33
Is any of this possible?
function myformat($nr)
{
$nr = number_format($nr, 2);
return $nr[0] == '-' ? "(" . substr($nr, 1) . ")" : $nr;
}
Add a space at the end of the positive number, to match the close parenthesis:
function myformat($nr)
{
$nr = number_format($nr, 2);
return $nr[0] == '-' ? "(" . substr($nr, 1) . ")" : $nr . ' ';
}
I assume you're using printf() in the caller to right-align the results.

php - using strpos() to replace multiple comparisons in an if statement

Is it OK to replace multiple comparisons by using strpos()?
Example:
changing
if ( $linecounter == $extra1 || $linecounter == $extra2 || $linecounter == 5 )
{
...
}
to
$ok = ' ' . $extra1 . $extra2 . '5';
if ( strpos($ok, $linecounter) > 0 )
{
...
}
No, it's not OK because that's not what strpos is for. It's easy to find problematic examples in the general case (e.g. $linecounter == 15 and $extra2 == 1 -- the strpos check will succeed when it should not have).
If you want to lose the separate conditions so much, workable alternatives would be either a switch statement or in_array:
switch($linecounter) {
case $extra1: case $extra2: case 5:
// whatever
break;
}
if (in_array($linecounter, [$extra1, $extra2, 5])) {
// whatever
}
As an aside, using strpos with a greater-than-zero test like that is not good style. What you want to say is "if it's found in the string", so write exactly that and lose the "prefix a space" hack:
if ( strpos($extra1 . $extra2 . '5', $linecounter) !== false )

Categories