I want to format the credit cards like below when i display it,
Eg:
1234 4567 9874 1222
as
1xxx xxxx xxx 1222
Is there any formatting function like this in Yii ?
No - but there's nothing wrong with using straight PHP.
If you always want the 1st and the last 4 chars you can do something like this:
$last4 = substr($cardNum, -4);
$first = substr($cardNum, 0, 1);
$output = $first.'xxx xxxx xxxx '.$last4;
There are many ways to do this, nothing Yii specific
You could do it using str_split (untested):
$string = "1234 4567 1234 456";
$character_array = str_split($string);
for ($i = 1; $i < count($character_array) - 4; $i++) {
if ($character_array[$i] != " "){
$character_array[$i] = "x";
}
}
echo implode($character_array);
So we are creating an array of characters from the string called
$character_array.
We are then looping thru the characters (starting from position 1,
not 0, so the first character is visible).
We loop until the number of entries in the array minus 4 (so the last
4 characters are not replaced) We replace each character in the loop
with an 'x' (if it's not equal to a space)
We the implode the array back into a string
And you could also use preg_replace :
$card='1234 4567 9874 1222';
$xcard = preg_replace('/^([0-9])([- 0-9]+)([0-9]{4})$/', '${1}xxx xxxx xxxx ${3}', $card);
This regex will also take care of hyphens.
There is no in-built function in Yii.
Related
How can I get the first and the last digit of a number? For example 2468, I want to get the number 28. I am able to get the ones in the middle (46) but I can't do the same for the first and last digit.
For the digits in the middle I can do it
$substrmid = substr ($sum,1,-1); //my $sum is 2468
echo $substrmid;
Thank you in advance.
You can get first and last character from string as below:-
$sum = (string)2468; // type casting int to string
echo $sum[0]; // 2
echo $sum[strlen($sum)-1]; // 8
OR
$arr = str_split(2468); // convert string to an array
echo reset($arr); // 2
echo end($arr); // 8
Best way is to use substr described by Mark Baker in his comment,
$sum = 2468; // No need of type casting
echo substr($sum, 0, 1); // 2
echo substr($sum, -1); // 8
You can use substr like this:
<?php
$a = 2468;
echo substr($a, 0, 1).substr($a,-1);
You can also use something like this (without casting).
$num = 2468;
$lastDigit = abs($num % 10); // 8
However, this solution doesn't work for decimal numbers, but if you know that you'll be working with nothing else than integers, it'll work.
The abs bit is there to cover the case of negative integers.
$num = (string)123;
$first = reset($num);
$last = end($num);
Is there a term for the idea of storing large numbers as letters? For example let's say I have the (relatively small) number 138201162401719 and I want to shrink the number of characters (I know this does not help with saving disk space) to the fewest possible number of characters. There are 26 letters in the English alphabet (but i count them as 25 since we need a zero letter). If I start splitting up my large number into pieces that are each 25 or less I get:
13, 8, 20, 11, 6, 24, 0, 17, 19
If I then count the numbers of the alphabet a=0, b=1, c=2, d=3... I can convert this to:
NIULGYART
So I went from 15 digits long (138201162401719) to 9 characters long (NIULGYART). This could of course be easily converted back to the original number as well.
So...my first question is "Does this have a name" and my second "Does anyone have PHP code that will do the conversion (in both directions)?"
I am looking for proper terminology so that I can do my own research in Google...though working code examples are cool too.
This only possible if you're considering to store your number before processing as a string. Because you can't store huge number as integers. You will lost the precision (13820116240171986468445 will be stored as 1.3820116240172E+22) so the alot of digits are lost.
If you're considering storing the number as a string this will be your answer:
Functions used: intval, chr and preg_match_all.
<?php
$regex = '/(2[0-5])|(1[0-9])|([0-9])/';
$numberString = '138201162401719';
preg_match_all($regex, $numberString, $numberArray, PREG_SET_ORDER);
echo($numberString . " -> ");
foreach($numberArray as $value){
$character = chr (intval($value[0]) + 65);
echo($character);
}
?>
Demo
This is the result:
138201162401719 -> NIULGYART
Here's how I would do it:
Store the big number as a string and split it into an array of numbers containing one digit each
Loop through the array extract 2-digit chunks using substr()
Check if the number is less than 26 (in which case, it is an alphabet) and add them to an array
Use array_map() with chr() to create a new array of characters from the above array
Implode the resulting array to get the cipher
In code:
$str = '138201162401719';
$arr = str_split($str);
$i = 0; // starting from the left
while ($i < count($arr)) {
$n = substr($str, $i, 2);
$firstchar = substr($n, 0, 1);
if ($n < 26 && $firstchar != 0) {
$result[] = substr($str, $i, 2);
$i += 2; // advance two characters
} else {
$result[] = substr($str, $i, 1);
$i++; // advance one character
}
}
$output = array_map(function($n) {
return chr($n+65);
}, $result);
echo implode($output); // => NIULGYART
Demo.
As an alternative, you could convert the input integer to express it in base 26, instead of base 10. Something like (pseudocode):
func convertBase26(num)
if (num < 0)
return "-" & convertBase26(-num) // '&' is concatenate.
else if (num = 0)
return "A"
endif
output = "";
while (num > 0)
output <- ('A' + num MOD 26) & output // Modulus operator.
num <- num DIV 26 // Integer division.
endwhile
return output
endfunc
This uses A = 0, B = 1, up to Z = 25 and standard place notation: 26 = BA. Obviously a base conversion is easily reversible.
strtr() is a magnificent tool for this task! It replaces the longest match as is traverses the string.
Code: (Demo)
function toAlpha ($num) {
return strtr($num, range("A", "Z"));
}
$string = toAlpha("138201162401719");
echo "$string\n";
$string = toAlpha("123456789012345");
echo "$string\n";
$string = toAlpha("101112131415161");
echo "$string\n";
$string = toAlpha("2625242322212019");
echo "$string";
Output:
NIULGYART
MDEFGHIJAMDEF
KLMNOPQB
CGZYXWVUT
Just flip the lookup array to reverse the conversion: https://3v4l.org/YsFZu
Merged: https://3v4l.org/u3NQ5
Of course, I must mention that there is a vulnerability with converting a sequence of letters to numbers and back to letters. Consider BB becomes 11 then is mistaken for eleven which would traslate to L when converted again.
There are ways to mitigate this by adjusting the lookup array, but that may not be necessary/favorable depending on program requirements.
And here is another consideration from CodeReview.
I have been trying to do the same thing in PHP without success.
Assuming I'm using the 26 letters of the English alphabet, starting with A = 0 down to Z as 25:
I find the highest power of 26 lower than the number I am encoding. I divide it by the best power of 26 I found. Of the result I take away the integer, convert it to a letter and multiply the decimals by 26. I keep doing that until I get a whole number. It's ok to get a zero as it's an A, but if it has decimals it must be multiplied.
For 1 billion which is DGEHTYM and it's done in 6 loops obviously. Although my answer demonstrates how to encode, I'm afraid it does not help doing so on PHP which is what I'm trying to do myself. I hope the algorithm helps people out there though.
I am looking for ways to split a string of a unicode alpha-numeric type to fixed lenghts.
for example:
992000199821376John Smith 20070603
and the array should look like this:
Array (
[0] => 99,
[1] => 2,
[2] => 00019982,
[3] => 1376,
[4] => "John Smith",
[5] => 20070603
)
array data will be split like this:
Array[0] - Account type - must be 2 characters long,
Array[1] - Account status - must be 1 character long,
Array[2] - Account ID - must be 8 characters long,
Array[3] - Account settings - must be 4 characters long,
Array[4] - User Name - must be 20 characters long,
Array[5] - Join Date - must be 8 characters long.
Or if you want to avoid preg:
$string = '992000199821376John Smith 20070603';
$intervals = array(2, 1, 8, 4, 20, 8);
$start = 0;
$parts = array();
foreach ($intervals as $i)
{
$parts[] = mb_substr($string, $start, $i);
$start += $i;
}
$s = '992000199821376Николай Шмидт 20070603';
if (preg_match('~(.{2})(.{1})(.{8})(.{4})(.{20})(.{8})~u', $s, $match))
{
list (, $type, $status, $id, $settings, $name, $date) = $match;
}
Using the substr function would do this quite easily.
$accountDetails = "992000199821376John Smith 20070603";
$accountArray = array(substr($accountDetails,0,2),substr($accountDetails,2,1),substr($accountDetails,3,8),substr($accountDetails,11,4),substr($accountDetails,15,20),substr($accountDetails,35,8));
Should do the trick, other than that regular expressions (as suggested by akond) is probably the way to go (and more flexible). (Figured this was still valid as an alternate option).
It is not possible to split a unicode string in a way you ask for.
Not possible without making the parts invalid.
Some code points have no way of standing out, for example: שׁ is 2 code points (and 4 bytes in UTF-8 and UTF-16) and you cannot split it because it is undefined.
When you work with unicode, "character" is a very slippery term. There are code points, glyphs, etc. See more at http://www.utf8everywhere.org, the part on "length of a string"
I have a list of phrases and I want to know which two words occurred the most often in all of my phrases.
I tried playing with regex and other codes and I just cannot find the right way to do this.
Can anyone help?
eg:
I am purchasing a wallet
a wallet for 20$
purchasing a bag
I'd know that
a wallet occurred 2 times
purchasing a occurred 2 times
<?
$string = "I am purchasing a wallet a wallet for 20$ purchasing a bag";
//split string into words
$words = explode(' ', $string);
//make chunks block ie [0,1][2,3]...
$chunks = array_chunk($words, 2);
//remove first array element
unset($words[0]);
//make chunks block ie [0,1][2,3]...
//but since first element is removed , the real block will be [1,2][3,4]...
$alternateChunks = array_chunk($words, 2);
//merge both chunks
$totalChunks = array_merge($chunks,$alternateChunks);
$finalChunks = array();
foreach($totalChunks as $t)
{
//change the inside chunk to pharse using +
//+ can be replaced to space, if neeced
//to keep associative working + is used instead of white space
$finalChunks[] = implode('+', $t);
}
//count the words inside array
$result = array_count_values($finalChunks);
echo "<pre>";
print_r($result);
I hesitate to suggest this, as it's an extremely brute force way to go about it:
Take your string of words, explode it using the explode(" ", $string); command, then run it through a for loop checking every two word combination against every two words in the string.
$string = "I am purchasing a wallet a wallet for 20$ purchasing a bag";
$words = explode(" ", $string);
for ($t=0; $t<count($string); $t++)
{
for ($i=0; $i<count($string); $i++)
{
if (($words[$t] . words[$t+1]) == ($words[$i] . $word[$i+1])) {$count[$words[$i].$words[$i+1]]++}
}
}
So the nested for loop steps in, grabs the first two words, compares them to each other set of two consecutive words, then grabs the next two words and does it again. Every answer will have an answer of at least 1 (it will always match itself) but sorting the resulting array by size will give you the most repeated values.
Note that this will run (n-1)*(n-1) iterations, which could get unwieldy FAST.
Place them all into an array, and access them by the current word index and next word index.
I think this should do the trick. It will grab pairs of words, unless you are at the end of the string, where you'll get only one word.
$str = "I purchased a wallet because I wanted a wallet a wallet a wallet";
$words = explode(" ", $str);
$array_results = array();
for ($i = 0; $i<count($words); $i++) {
if ($i < count($words)-1) {
$pair = $words[$i] . " " . $words[$i+1]; echo $pair . "\n";
// Have to check if the key is in use yet to avoid a notice
$array_results[$pair] = isset($array_results[$pair]) ? $array_results[$pair] + 1 : 1;
}
// At the end of the array, just use a single word
else $array_results[$words[$i]] = isset($array_results[$words[$i]]) ? $array_results[$words[$i]] + 1 : 1;
}
// Sort the results
// use arsort() instead to get the highest first
asort($array_results);
// Prints:
Array
(
[I wanted] => 1
[wanted a] => 1
[wallet] => 1
[because I] => 1
[wallet because] => 1
[I purchased] => 1
[purchased a] => 1
[wallet a] => 2
[a wallet] => 4
)
Update changed ++ to +1 above since it wasn't working when tested...
Try to put it with explode into an array and count the values with array_count_values.
<?php
$text = "whatever";
$text_array = explode( ' ', $text);
$double_words = array();
for($c = 1; $c < count($text_array); $c++)
{
$double_words[] = $text_array[$c -1] . ' ' . $text_array[$c];
}
$result = array_count_values($double_words);
?>
I updated it now to two word version. Does this work for you?
array(9) {
["I am"]=> int(1)
["am purchasing"]=> int(1)
["purchasing a"]=> int(2)
["a wallet"]=> int(2)
["wallet a"]=> int(1)
["wallet for"]=> int(1)
["for 20$"]=> int(1)
["20$ purchasing"]=> int(1)
["a bag"]=> int(1)
}
Since you used the excel tag, I thought I'd give it a shot, and it's actually really easy.
Split string using space as delimiter. Data > Text to Columns... > Delimited > Delimiter: Space. Each word is now in its own cell.
Transpose the result (not strictly required but much easier to visualize). Copy, Edit > Paste Special... > Transpose.
Make cells containing consecutive word pairs. So if your words are in cells B5:B15, cell C5 should be =B5&" "&B6 (and drag down).
Count occurence of each word pair: In cell D5, =COUNTIF($C$5:$C$15,"="&C5), drag down.
Highlight the winner(s). Select C5:D15, Format > Conditional Formatting... > Formula Is =$D5=MAX($D$5:$D$15) and choose e.g. a yellow background.
Note that there is some inefficiency in step 4 because the count of each word pair will be calculated multiple times if that word pair occurs multiple times. If this is a concern, then you can first make a list of unique word pairs using Data > Filter > Advanced Filter... > Unique records only.
An automated VBA solution could easily be crafted by recording a macro of the above followed by some minor editing.
One way to go about it is to use SPLIT or a regex to split the sentences into words and store each into an array. Then take the array and create a dictionary object. When you add a term to the dictionary, if it's already there, add 1 to the .value to tally the count.
Here is some example code (far from perfect as it's just to show the overlying concept) that will take all the string in column A and generate a word frequency list in columns B and C. It's not exactly what you want, but should give you some ideas on how you can go about doing it I hope:
Sub FrequencyList()
Dim vArray As Variant
Dim myDict As Variant
Set myDict = CreateObject("Scripting.Dictionary")
Dim i As Long
Dim cell As range
With myDict
For Each cell In range("A1", cells(Rows.count, "A").End(xlUp))
vArray = Split(cell.Value, " ")
For i = LBound(vArray) To UBound(vArray)
If Not .exists(vArray(i)) Then
.Add vArray(i), 1
Else
.Item(vArray(i)) = .Item(vArray(i)) + 1
End If
Next
Next
range("B1").Resize(.count).Value = Application.Transpose(.keys)
range("C1").Resize(.count).Value = Application.Transpose(.items)
End With
End Sub
This is kind of a follow on from this post: Regex for splitting params out using preg_match
I have this string 1 0 61 12345678 sierra007^7 0 0 123.123.123.123:524 26429 25000 and I need to get each element. It was suggested I use explode which was a great simple solution but now I need to allow spaces in one of the fields.
Someone else posted this regex:
/^([-0-9]+)\s+([-0-9]+)\s+([-0-9]+)\s+([-0-9]+)\s+(\S+)\s+([-0-9])\s+([-0-9]+)\s+([-0-9.:]+)\s+([-0-9.]+)\s+([-0-9.]+)/mx
That does everything else and I was wondering if it could be modified to allow spaces in field 5 (sierra007^7). The only advice I can offer is that the rest of the fields are always numeric (or a colon as you can see) before and after field 5. Is this possible with 1 regex statement or do I need to parse it in PHP and fudge it together?
EDIT: For example, field 5 could be sierra007^7 OR si erra007^7 or si er ra007^7. It would know that it came across field 5 as its the only one that contains a-zA-Z characters. It would know where field 5 ends because field 6 only contains 0-9 characters.
Thanks.
Why not use explode, like the other thread. And count the number of items in the array. If more items are in the array, you put item 5 + any number too high together again with implode..
Eg. your normal row has 10 items. If the resulting explode has 15 items, you:
implode(" ",array_slice($array,5,(count($array)-10)));
If the number of fields never changes, and there's always a value for each field, you can do it using code below:
$fields = explode (' ', $str);
$defaultNumFields = 10;
if (count($fields) > $defaultNumFields) {
for ($i = 5; $i < (count($fields) - $defaultNumFields) + 5; $i++) {
$field[4] .= ' '.$field[$i];
unset($field[$i]);
}
}
$fields = array_values($fields);
That should do it. I might have mis-calcuated and you might need to change the +4 to a +5, test it on a few strings and let me know.