I am trying to do chord transposition in PHP the array of Chord values are as followed...
$chords1 = array('C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','Db','D','Eb','E','F','Gb','G','Ab','A','Bb','B','C');
An example would be D6/F#. I want to match the array value and then transpose it by a given number position in the array. Here is what I have so far...
function splitChord($chord){ // The chord comes into the function
preg_match_all("/C#|D#|F#|G#|A#|Db|Eb|Gb|Ab|Bb|C|D|E|F|G|A|B/", $chord, $notes); // match the item
$notes = $notes[0];
$newArray = array();
foreach($notes as $note){ // for each found item as a note
$note = switchNotes($note); // switch the not out
array_push($newArray, $note); // and push it into the new array
}
$chord = str_replace($notes, $newArray, $chord); // then string replace the chord with the new notes available
return($chord);
}
function switchNotes($note){
$chords1 = array('C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','Db','D','Eb','E','F','Gb','G','Ab','A','Bb','B','C');
$search = array_search($note, $chords1);////////////////Search the array position D=2 & F#=6
$note = $chords1[$search + 4];///////////////////////then make the new position add 4 = F# and A#
return($note);
}
This works, except the problem is that if I use a split chord like (D6/F#) The chord is transposed to A#6/A#. It is replacing the first note (D) with an (F#) then, Both (F#'s) with an (A#).
The question is... How can I keep this redundancy from happening. The desired output would be F#6/A#. Thank you for your help. If the solution is posted, I WILL mark it as answered.
You can use preg_replace_callback function
function transposeNoteCallback($match) {
$chords = array('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B', 'C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B', 'C');
$pos = array_search($match[0], $chords) + 4;
if ($pos >= count($chords)) {
$pos = $pos - count($chords);
}
return $chords[$pos];
}
function transposeNote($noteStr) {
return preg_replace_callback("/C#|D#|F#|G#|A#|Db|Eb|Gb|Ab|Bb|C|D|E|F|G|A|B/", 'transposeNoteCallback', $noteStr);
}
Test
echo transposeNote("Eb6 Bb B Ab D6/F#");
returns
G6 C# Eb C F#6/A#
Cheap advice: move into natural numbers domain [[0-11]] and associate them with corresponding notes at display time only, it will save you many time.
The only problem will be homophones sounds [e.g. C-sharp / D-flat], but hope you can deduce it from tonality.
Related
Given a custom alphabet like ['f', 'h', 'z', '#', 's']
I'd like to take a string written in this "alphabet" like
ffff#zz and increment it
So for example if the string was sss After incrementing it will look like hfff the same way that if you increment 999 you get 1000
My current attempt is here: https://3v4l.org/FjAsd
Given:
$characters = ['a', 'b', 'c'];
$string = 'cccc';
my code can do:
baaaa
but if give it
$characters = ['a', 'b', 'c'];
$string = 'aaa';
it will return
b
When I expected aab
The way this works is by processing the string from the end, each time you look at a character you check it's position in the array (I use a flipped array as it's more efficient than using array_search() each time). Then if the character is at the end of the array, then set it to the 0th element of the alphabet and increment the next digit to the left. If there is another letter from the alphabet to increment the current value, then just replace it and stop the loop.
The last bit is that if you have processed every character and the loop was still going, then there is a carry - so add the 0th digit to the start.
$characters = ['a', 'b', 'c'];
$string = 'cccc';
$index = array_flip($characters);
$alphabetCount = count($index)-1;
for ( $i = strlen($string)-1; $i >= 0; $i--) {
$current = $index[$string[$i]]+1;
// Carry
if ( $current > $alphabetCount ) {
$string[$i] = $characters[0];
}
else {
// update and exit
$string[$i] = $characters[$current];
break;
}
}
// As reached end of loop - carry
if ( $i == -1 ) {
$string = $characters[0].$string;
}
echo $string;
gives
aaaaa
with
$characters = ['f', 'h', 'z', '#', 's'];
$string = 'ffff#zz';
you get
ffff#z#
I ended up with something like this:
$string = 'ccc';
$alphabet = ['a', 'b', 'c'];
$numbers = array_keys($alphabet);
$numeric = str_replace($alphabet, $numbers, $string);
$base = count($alphabet) + 1;
$decimal = base_convert($numeric, $base, 10);
$string = base_convert(++$decimal, 10, $base);
strlen($decimal) !== strlen($string)
and $string = str_replace('0', '1', $string);
echo str_replace($numbers, $alphabet, $string);
This one has the advantage of supporting multi byte characters
I have 6 variables and each other have integer values, I want to get the variable name which have the greatest value.
The variables are: $a = 2, $b = 3, $c = 3, $d = 4, $e = 4, $f = 4
If I use this code:
$var = compact('a', 'b', 'c', 'd', 'e', 'f');
arsort($var);
$name = key($var);
The variable $name will contain only $d. The question is, how to get $d, $e, $f?
Try array_keys() with the search argument:
$var = compact('a', 'b', 'c', 'd', 'e', 'f');
arsort($var);
$max = reset($var); // get the maximum value (first item)
$results = array_keys($var, $max, true); // search for all the maximums and return the keys
Or use max() without sorting:
$var = compact('a', 'b', 'c', 'd', 'e', 'f');
$max = max($var); // get the maximum value
$results = array_keys($var, $max, true); // search for all the maximums and return the keys
I need to check if element 'a' is present twice, 'b' is present twice and 'c' is present once in a group of arrays. Each element should be in five different arrays.
That is like, 'a' in array1, another 'a' in array2, 'b' in array3, another 'b' in array4 and 'c' in array5. there should be atleast five or more arrays and each element should be in different arrays using php. Now my code is
$arr = array(
$branch1 = array('a', 'b'),
$branch2 = array('b','c'),
$branch3 = array('a', 'c'),
$branch4 = array('c', 'a'),
$branch5 = array('b', 'a'),
$branch6 = array('b', 'c', 'a')
);//This may have any number of branches and any kind of combinations of a, b and c(but each element only once in each array).
$reqd_branch_count = 5;//required branch count
As I am new to php, now I have written a very long code, but it fails when trying new combinations.Please help me if someone knows.
From asker's comment:
The condition is a user has some ranks in his/her downline which are
in branches. for example rank1, rank2 and rank3,.... Those rank counts
are shown in that array. If that user need to get rank8 he/she should
have one rank 7, two rank 5 or 7s, and two rank4s, that should be
1+2+2=5 branches, that is each rank should be in different branches.
Hope you understood the question
if i understood correctly
merge all arrays, count amount of all items in all arrays and test you want
$arr = array_merge($branch1,$branch2,$branch3,$branch4,$branch5,$branch6);
$count = array_count_values($arr);
echo $count[7]; // 4
echo $count[4]; // 2
so, yuo can make condition
if (($count[7] == 1) or ($count[7] == 2) or ($count[5] == 2) or ($count[4] == 2))
{ any stuff for true}
UPDATE
$arr = array(
$branch1=array('3'),
$branch2=array('4'),
$branch3=array('3','5','7'),
$branch4=array('3','4','7'),
$branch5=array('7'),
$branch6=array('4','7'));
// find rank per branch
$ranks = array_map('max', $arr);
// make array rank => amount
$count = array_replace(array_fill(0,7,0), array_count_values($ranks));
if (($count[7] >= 1) and (($count[7] + $count[5]) >= 2) and (($count[7] + $count[5] + $count[4]) >= 5)) {
echo "Satisfy ";
}
Because it is another question, this is another answer
$arr = array(
$branch1 = array('a'),
$branch2 = array('b','c'),
$branch3 = array('a', 'c'),
$branch4 = array('c', 'a'),
$branch5 = array('a'),
$branch6 = array( 'c', 'a')
);
$names = array('a', 'b', 'c'); // for convenience only
var_dump(goNext($arr, $names)); // watch result
function goNext($arr, $names) {
// for each name make array with list of brances where it is
$in = array(array(), array(), array());
foreach($arr as $k1 => $branch)
foreach($names as $k2 => $letter)
if(in_array($letter, $branch)) $in[$k2][] = $k1;
foreach ($in[0] as $i1) // 1st a
foreach (array_diff($in[0], array($i1)) as $i2) // 2nd a
foreach (array_diff($in[1], array($i1,$i2)) as $i3) // 1st b
foreach (array_diff($in[1], array($i1,$i2,$i3)) as $i4) // 2nd b
foreach(array_diff($in[2], array($i1,$i2,$i3,$i4)) as $i5) {
// if here we find combination we need
// next line only for debug
// it shows set of branches that give true
// a a b b c
echo $i1 . " " . $i2 . " " . $i3 . " " . $i4 . " " . $i5;
return(true);
}
return(false); // combination has not found
}
Its working nicely:
$str = 'a';
echo ++$str; // prints 'b'
$str = 'z';
echo ++$str; // prints 'aa'
Its very useful to get next column name in an excel file.
But if I use similar code using -- operator to get the previous letter then its not working:
$str = 'b';
echo --$str; // prints 'b' but I need 'a'
$str = 'aa';
echo --$str; // prints 'aa' but I need 'z'
What can be the solution to get the previous letter similarly?
And what can be the reason as its not working?
$str='z';
echo chr(ord($str)-1); //y
Note: This isn't circular for a-z. Need to add rules for that
Fiddle
Edit
This edit covers for your special requirement from excel example. Although its a little longer piece of code.
//Step 1: Build your range; We cant just go about every character in every language.
$x='a';
while($x!='zz') // of course you can take that to zzz or beyond etc
{
$values[]=$x++; // A simple range() call will not work for multiple characters
}
$values[]=$x; // Now this array contains range `a - zz`
//Step 2: Provide reference
$str='ab';
//Step 3: Move next or back
echo $values[array_search(strtolower($str),$values)-1]; // Previous = aa
echo $values[array_search(strtolower($str),$values)+1]; // Next = ac
Fiddle
Using array:
$cla=array('A', 'B', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'R', 'S', 'Š', 'Z', 'Ž', 'T', 'U', 'V', 'Õ', 'Ä', 'Ö', 'Ü');
$direction = -1;
$element = 'Ü';
$element2 = $cla[((array_search($element,$cla)+count($cla)+$direction)%count($cla))];
I could solve in this way.
How is it?
The cons is that it only can handle uppercase right now.
Some more work can also fix that.
<?php
function get_previous_letter($string){
$last = substr($string, -1);
$part=substr($string, 0, -1);
if(strtoupper($last)=='A'){
$l = substr($part, -1);
if($l=='A'){
return substr($part, 0, -1)."Z";
}
return $part.chr(ord($l)-1);
}else{
return $part.chr(ord($last)-1);
}
}
echo get_previous_letter("AAAAAA");
?>
CODEPAD
I have an array of letters and I want to pick 5 unique ones.
$variable_array = array('x', 'n', 'f', 'w', 'g', 'r', 'c', 'm', 'y', 'u', 'p', 'a', 'd', 'h', 'k', 'z');
So, I want something like this...
$variable_1 = $variable_array[array_rand($variable_array)];
$variable_2 = $variable_array[array_rand($variable_array)];
$variable_3 = $variable_array[array_rand($variable_array)];
$variable_4 = $variable_array[array_rand($variable_array)];
$variable_5 = $variable_array[array_rand($variable_array)];
but all of the variables need to be unique. I know there are other answers similar to this on Stackoverflow already, but they all seem to be slightly different than my situation. Any ideas?
PHP shuffle(): http://php.net/manual/en/function.shuffle.php
// make sure you have only unique values in your array
// $variable_array = array_unique($variable_array);
// randomize array
shuffle($variable_array);
// get 5 elements like suggested in the comments:
// $top5_array = array_slice($variable_array, 0, 5);
$top5_array = ARRAY();
// do 5 times ...
//
// if (is_array($variable_array) AND count($variable_array) >=5) {
//
for ($i=0; $i<=4; $i++) {
$top5_array[] = $variable_array[$i];
print $variable_array[$i].'<br />';
}
var_dump($top5_array);