I would like to encode a binary sequence to a DNA sequence by following this rule:
00 = A
01 = C
10 = G
11 = T
For example: 10011100 = GCTA.
I wrote a PHP script which converts my string to binary and then I convert the binary to a DNA sequence by using the str_replace function. My issue is that the DNA sequence is not correctly converted. Can someone help me?
Here's my PHP script:
<?php
// Function to convert string to bin
function strToBin($input) {
if (!is_string($input))
return false;
$ret = '';
for ($i = 0; $i < strlen($input); $i++) {
$temp = decbin(ord($input{$i}));
$ret .= str_repeat('0', 8 - strlen($temp)) . $temp;
}
return $ret;
}
$bin = strToBin('Stack');
// Try to transcript binary to DNA
$bincomb = array('00', '01', '10', '11');
$DNAtrans = array('A', 'C', 'G', 'T');
echo $transcript = str_replace($bincomb, $DNAtrans, $bin);
?>
The str_replace() approach doesn't work because it looks for the substrings in the order 00, 01, 10, 11. So, for example, with the binary string 1001 it replaces the inner 00 with an A, after which the string is 1A1, and no more conversions can be made.
For your function to work, you need to go through the binary string in chunks of two characters.
$bin = strToBin('Stack');
$bin = str_split($bin, 2);
$combine = array(
'00' => 'A',
'01' => 'C',
'10' => 'G',
'11' => 'T'
);
$str = '';
foreach ($bin as $item)
$str .= $combine[$item];
The string is first split into chunks of two characters, which are then looped through and their respective values appended to the result string $str.
In PHP, I usually do these transformations with the two-arguments implementation of strtr(), like this:
// outputs 10011100
echo strtr("GCTA", array("A" => "00", "C" => "01", "G" => "10", "T" => "11"));
The reverse is also possible:
// outputs GCTA
echo strtr("10011100", array("00" =>"A", "01" => "C", "10" => "G", "11" => "T"));
Related
I have this string:
$string = 'Hello IV WorldX';
And I want to replace all roman numerals to integers.
I have the following function to convert roman to integer:
function roman2number($roman){
$conv = array(
array("letter" => 'I', "number" => 1),
array("letter" => 'V', "number" => 5),
array("letter" => 'X', "number" => 10),
array("letter" => 'L', "number" => 50),
array("letter" => 'C', "number" => 100),
array("letter" => 'D', "number" => 500),
array("letter" => 'M', "number" => 1000),
array("letter" => 0, "number" => 0)
);
$arabic = 0;
$state = 0;
$sidx = 0;
$len = strlen($roman);
while ($len >= 0) {
$i = 0;
$sidx = $len;
while ($conv[$i]['number'] > 0) {
if (strtoupper(#$roman[$sidx]) == $conv[$i]['letter']) {
if ($state > $conv[$i]['number']) {
$arabic -= $conv[$i]['number'];
} else {
$arabic += $conv[$i]['number'];
$state = $conv[$i]['number'];
}
}
$i++;
}
$len--;
}
return($arabic);
}
echo roman2number('IV');
Works great (try it on ideone). How do I search & replace through the string to replace all instances of roman numerals. Something like:
$string = romans_to_numbers_in_string($string);
Sounds like regex needs to come to the rescue... or?
Here's a simple regex to match roman numerals:
\b[0IVXLCDM]+\b
So, you can implement romans_to_numbers_in_string like this:
function romans_to_numbers_in_string($string) {
return preg_replace_callback('/\b[0IVXLCDM]+\b/', function($m) {
return roman2number($m[0]);
},$string);
}
There are some problems with this regex. Like, if you have a string like this:
I like roman numerals
It will become:
1 like roman numerals
Depending on your requirements, you can let it be, or you can modify the regex so that it doesn't convert single I's to numbers.
I calculated modulo mathematics with the formula: 3 * n mod 26
by encrypting the array [n] A-Z to the conversion of numbers, the results of the numbers are obtained from the results of a predetermined array, then the calculated results are converted back into a string.
Example: MY NAME IS CARL > KU NAKM YC GAZH
K = 10, U = 20, N = 13, A = 0, K = 10, M = 12
Y = 24, C = 2, G = 6, A = 0, Z = 25, H = 7
I have managed to convert numbers into strings and get a result: KUANAKMAYCAGAZH
What I want to ask is how to delete 'A' by replacing spaces, so the result is: 'KU NAKM YC GAZH' not 'KUANAKMAYCAGAZH'
Sorry, for my bad English.
Below is my script:
<?php
$text = 'MY NAME IS CARL';
$str = '';
$key = array(
"A" => 0, "B" => 1,"C" => 2, "D" => 3,"E" => 4,"F" => 5,
"G" => 6, "H" => 7, "I" => 8, "J" => 9, "K" => 10,
"L" => 11, "M" => 12, "N" => 13, "O" => 14,
"P" => 15, "Q" => 16, "R" => 17, "S" => 18,"T" => 19,
"U" => 20, "V" => 21, "W" => 22, "X" => 23,
"Y" => 24, "Z" => 25
);
for ($i = 0; $i < strlen($text); $i++) {
$number = (3*$key[strtoupper($text[$i])])%26; // math caesar cipher 3 * n modulo 26
$str .= array_search($number, $key);
}
echo $str;
?>
At the moment, you will get an error while trying to lookup the spaces in your array, you will get a message saying...
PHP Notice: Undefined index: in...
if you had error reporting turned on.
To solve this you can check that the character exists in the array before trying to encode it, otherwise just copy the character...
for ($i = 0; $i < strlen($text); $i++) {
if ( isset($key[strtoupper($text[$i])]) ) {
$number = (3*$key[strtoupper($text[$i])])%26; // math caesar cipher 3 * n modulo 26
$str .= array_search($number, $key);
}
else {
$str .= $text[$i];
}
}
Rather than using an array of character => integer conversions, you could use the built-in ord and chr functions to create a translation function:
function translate_char($c) {
$o = ord($c);
if (in_array($c, range('A', 'Z'))) {
return chr((($o - 65) * 3 % 26) + 65);
}
elseif (in_array($c, range('a', 'z'))) {
return chr((($o - 97) * 3 % 26) + 97);
}
else {
return $c;
}
}
This function also deals with lowercase alphabetic characters; if that is not required, simply remove the elseif clause. Any characters which are not alphabetic are returned unchanged.
You can then use array_map to apply this function to all the characters in $text:
$text = 'My name is Carl';
$str = implode('', array_map('translate_char', str_split($text)));
echo $str;
Output:
Ku nakm yc Gazh
Demo on 3v4l.org
I am trying to figure out how I can start looping through an array at a different index but when it reaches the end it loops back to the beginning and finishes the array. Basically, I need to be able to dynamically change the offset of the array.
What I am trying to do it associate a letter of the alphabet with a different alphabet letter to mix things up for a string.
Let's say I have a random array like so
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
Then I have a string like so
$string = 'abcde';
And let's say I need to start at index in the array at 2 which would be 'c' => 'j' then finish the array to the end and then loop back to the beginning until it is finished.
What I want to do is replace each letter with the corresponding letter associated with it in the array. So the final string after it is replaced would look like
I would reconstruct the array with
$build = strtr($string,$arr);
which would echo gwjyk
But I need to start at a random point in the array and then finish it and go back to the beggining and finish the entire array.
So maybe I have an offset of 2.
$offset = 2;
As I mentioned in the comments, I would approach this using array_slice and then merging the two arrays in order to simply get a new array, then loop through it from start to finish.
Here's a fully functional solution (and a runnable version)- although I'd like to point out that the offset really doesn't change the results at all:
/**
* Goes through a string and replaces letters based on an array "map".
*
* #param string - $string
* #param int - $offset
*
* #return string
*/
function change_letters( $string, $offset ) {
$letters = ['a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k'];
// some defensive code to prevent notices or errors
if ( (int)$offset > count($letters)) {
echo '<p>Error: Offset is larger than the array of letters!</p>';
return $string;
}
// build new array based on passed-in offset
$new_array = array_slice($letters, $offset) + array_slice($letters, 0, $offset);
// at this point, array is ['c' => 'j', 'd' => 'y', 'e' => 'k', 'a' => 'g', 'b' => 'w']
// loop through the letters to replace...
foreach($new_array AS $from => $to) {
// swaps all instances of the "from" letter to the "to" letter in the string.
// NOTE: this could be easily modified to only replace n instances of the "from" letter
// like so: $string = str_ireplace( $from, $to, $string, 1); - would only replace 1 instance
$string = str_ireplace( $from, $to, $string );
}
return $string;
}
// Sample usage:
$word = 'abcde';
$new_word = change_letters( $word, 2); // "gwjk"
var_dump(2, $new_word);
$new_word = change_letters( $word, 5); // "gwjk"
var_dump(5, $new_word);
$new_word = change_letters( $word, 6); // "abcde"
var_dump(5, $new_word);
You can try:
<?php
$arr = array(1 => 2, 3 => 4, 5 => 6, 7 => 8, 9 => 0);
$STARTING_KEY = 3;
$array_keys = array_keys($arr);
$starting_index = array_search($STARTING_KEY, $array_keys);
for ($i = $starting_index; $i < sizeof($arr); $i++) {
echo $arr[$array_keys[$i]] . "\n";
}
for ($i = 0; $i < $starting_index; $i++) {
echo $arr[$array_keys[$i]] . "\n";
}
This will test all possible offsets for the string
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$str = "abcde";
$strlen = strlen($str);
$keys = array_keys($arr);
for ($j = 0; $j < $strlen; $j++)
{
$startIndex = $j;
echo "offset: " . $startIndex . ": ";
for ($i = $startIndex; $i < $strlen; $i++ )
{
$char = substr( $str, $i, 1 );
echo $arr[$char];
}
for ($i = 0; $i < $startIndex; $i++ )
{
$char = substr( $str, $i, 1 );
echo $arr[$char];
}
echo "\n";
}
Output:
offset: 0: gwjyk
offset: 1: wjykg
offset: 2: jykgw
offset: 3: ykgwj
offset: 4: kgwjy
As mentioned in the comment, another option for your example data could be using array_slice and setting the offset and the length parameters and use array_merge:
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$top = array_slice($arr, 0, 2);
$rest = array_slice($arr, 2);
print_r(array_merge($rest, $top));
Array
(
[c] => j
[d] => y
[e] => k
[a] => g
[b] => w
)
All that array slicin’n’dicing or using two loops to loop from x to end first, and start up to x second, is fine … but they don’t make for the most readable code IMHO.
Such an “offsetted circling-through” can be achieved in a quite trivial way with a numerically indexed array - a simple for loop, and the index “clamped down” by using modulo with the total number of array elements.
So in a case like this, I would perhaps prefer the following approach:
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$c = count($arr);
$search = array_keys($arr);
$replace = array_values($arr);
$offset = 2; // zero-based
for( $i = 0; $i < $c; ++$i ) {
$idx = ( $i + $offset ) % $c;
echo $search[$idx] . ' => ' . $replace[$idx] . "<br>\n";
}
// result:
// c => j
// d => y
// e => k
// a => g
// b => w
I have this string of random numbers that needs to have some of the integers converted into letters. Notice that it takes two positions in the string to make a letter.
01110413 Original string
This string should ultimately be converted into this:
A11D13 Converted string
Here is my code so far
$id = '01110413';
$chg = array(0 => array(0, 1), 3 => array(4, 5));
$ltr = array(00 => 'A', 01 => 'B', 03 => 'C', 04 => 'D');
$id = str_split($id);
foreach($chg as $ltrpos => $val){
// $ltrpos; letter position placement in string AFTER conversion to letter
$ltrkey = null;
foreach($val as $idkey){
$ltrkey .= $id[$idkey];
unset($id[$idkey]);
if(!empty($ltrkey)){
$out[$ltrpos] = $ltr[(INT)$ltrkey];
}
}
}
Running this code gives:
Array
(
[0] => B
[3] => D
)
I need to insert these new values in the place where the old integer values were. The $chg array key is the position where the values should be in the converted string.
How can I order my final $out array so that the integers that were unset are replaced with the converted letters in their place?
This should do it:
$id = '01110413';
// your string codes always take up two positions, so you just need to provide the final position in the string
$chg = array(0,3);
// You could actually change 00 to just 0 (since they're integers). Also, later in the script, the two character position is cast to an int, so it will match these values.
$ltr = array(00 => 'A', 01 => 'B', 03 => 'C', 04 => 'D');
$converted_id = doConvert($id, $ltr, $chg);
function doConvert($id, $conversion_codes, $final_position) {
if( count($final_position) == 0 ) return $id;
$next_pos = array_shift($final_position);
// convert the two characters at the next position to a letter
$id = substr($id, 0, $next_pos) . $conversion_codes[(int) substr($id, $next_pos, 2)] . substr($id, $next_pos+2); // (cast to an (int) so it will match the keys in our conversion array)
return doConvert($id, $conversion_codes, $final_position);
}
Output of this example is:
B11D13
You say the first value should be A, but 01 => B, that's why the first letter is B.
If every two characters in your original id is a code, you can use something more general, like this:
$id = '01110413';
$conversion = array('00' => 'A', '01' => 'B', '03' => 'C', '04' => 'D');
$converted_id = "";
for($i=0; $i < strlen($id); $i+=2) {
$char_code = substr($id, $i, 2);
// we don't know this code, just append it
if( empty($conversion[$char_code]) ) {
$converted_id .= $char_code;
}
// convert and append
else {
$converted_id .= $conversion[$char_code];
}
}
Let's say I have these two related arrays:
$letters = ['a', 'b', 'c', 'd', 'e'];
$replace = [1, 5, 10, 15, 20];
And a string of letters separated by spaces.
$text = "abd cde dee ae d";
I want to convert consecutive letters to their respective numbers, sum the numbers, then replace the original letters with the total.
Using str_replace() isn't right because the values are compressed as a string before I can sum them.
$re = str_replace($letters, $replace, $text);
echo $re; //this output:
1515 101520 152020 120 15
I actually want to sum the above numbers for each "word" and the result should be:
21 45 55 21 15
What I tried:
$resultArray = explode(" ", $re);
echo array_sum($resultArray).'<br />';
It incorrectly outputs:
255190
EDIT:
My actual data contains arabic multibyte characters.
$letters = array('ا', 'ب','ج','د' ) ;
$replace = array(1, 5, 10, 15 ) ;
$text = "جا باب جب";
Convert the string into an array and use array_sum.
array_sum(explode(' ', $re));
Edit
Sorry, misunderstood:
$letters = array('a','b','c', 'd', 'e');
$replace = array( 1, 5, 10, 15 , 20);
$text = "abd cde dee ae d" ;
$new_array = explode(' ', $text);
$sum_array = array();
foreach ($new_array as $string)
{
$nums = str_split($string);
foreach ($nums as &$num)
{
$num = str_replace($letters, $replace, $num);
}
$sum_array[] = array_sum($nums);
}
echo implode(' ', $sum_array);
Instead of replacing the letters with numbers I would suggest just looking up the letters in the replace array one at a time:
EDIT
<?php
$text = "abd cde dee ae d";
$replace = array('a' => 1, 'b' => 5, 'c' => 10, 'd' => 15, 'e' => 20);
$letters = str_split($text);
$sums = array(0);
foreach ($letters as $letter) {
// Add a new element to the sum array.
if ($letter == ' ') {
$sums[] = 0;
} else {
$sums[count($sums) - 1] += $replace[$letter];
}
}
echo implode(" ", $sums);
?>
Here is a working example:
http://codepad.org/Cw71zuKD
Because the summed results after translating sequences of letters is meant to be used as the replacement text in the output string, I find preg_replace_callback() to be a fitting tool to encapsulate and deliver the required logic.
Isolate a substring of letters
Split each respective set of letters into an array of individual characters
Perform the necessary translation to integer values
Sum the array of numbers
Replace the original substring with the summed value
Repeat until all substrings are replaced
Code: (Demo)
$letters = ['a','b','c', 'd', 'e'];
$replace = [1, 5, 10, 15 , 20];
$text = "abd cde dee ae d";
echo preg_replace_callback(
'/[a-e]+/',
fn($m) => array_sum(
str_replace(
$letters,
$replace,
str_split($m[0])
)
),
$text
);
Output:
21 45 55 21 15
For multibyte processing: (Demo)
$letters = array('ا', 'ب','ج','د' ) ;
$replace = array(1, 5, 10, 15 ) ;
$text = "جا باب جب";
echo preg_replace_callback(
'/\S+/u',
fn($m) => array_sum(
str_replace(
$letters,
$replace,
mb_str_split($m[0])
)
),
$text
);
Output;
11 11 15