Related
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 an array.
$a = array(
0 => 1,
1 => 1,
2 => 2,
3 => 3,
4 => 1
);
How to get unique array like this?
$result = array_My_unique($a);
print_r($result);
Output:
$a = array(
0 => 1,
1 => 2,
2 => 3,
3 => 1
);
Thank!
Assuming you are trying to avoid duplicates that are immediately next to each other:
function array_my_unique($a = array()) {
$out = array();
$curr = false;
foreach ($a as $v) {
if ($curr !== $v) {
$out[] = $v;
}
$curr = $v;
}
return $out;
}
This satisfies the assertion between input/output that you described in the question.
I would like to take an array like this and combine it into 1 single array.
array (size=2)
0 =>
array (size=10)
0 => string '1'
1 => string 'a'
2 => string '3'
3 => string 'c'
1 =>
array (size=5)
0 => string '2'
1 => string 'b'
However I want the array results to be interleaved.
So it would end up looking like
array
0 => '1'
1 => '2'
2 => 'a'
3 => 'b'
4 => '3'
5 => 'c'
I would like it so that it doesn't matter how many initial keys are passed in (this one has 2), it should work with 1, 2 or 5. Also, as you can see from my example the amount of elements most likely won't match.
Anyone know the best way to accomplish this?
$data = array(
0 => array(
0 => '1',
1 => 'a',
2 => '3',
3 => 'c',
),
1 => array(
0 => '2',
1 => 'b',
),
);
$newArray = array();
$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
$mi->attachIterator(new ArrayIterator($data[0]));
$mi->attachIterator(new ArrayIterator($data[1]));
foreach($mi as $details) {
$newArray = array_merge(
$newArray,
array_filter($details)
);
}
var_dump($newArray);
I had fun with this... So if you like it use it!
$arr1 = [1,'a',3,'c'];
$arr2 = ['2','b'];
$finarry = arrayInterweave($arr1,$arr2);
print_r($finarry);
function arrayInterweave($arr1,$arr2){
$count1 = count($arr1);
$count2 = count($arr2);
$length = (($count1 >= $count2) ? $count1 : $count2);
$fin = array();
for($i = 0;$i<$length;$i++){
if(!empty($arr1[$i])){
$fin[] = $arr1[$i];
}
if(!empty($arr2[$i])){
$fin[] = $arr2[$i];
}
}
return $fin;
}
Tried to think of a fun solution:
$array = [
["a","b","c"],
["d","e"]
];
$result = [];
while($array) {
array_walk(
$array,
function(&$subarray, $key) use (&$array, &$result) {
$result[] = array_shift($subarray);
if(empty($subarray)) unset ($array[$key]);
}
);
}
var_dump($result);
It destroys the original array though.
After determining which row contains the most elements, you can loop through known indexes and push columns of data into the result array.
The following technique is safe to use with a variable number of rows.
Code: (Demo)
$maxCount = max(array_map('count', $array));
$result = [];
for ($i = 0; $i < $maxCount; ++$i) {
array_push($result, ...array_column($array, $i));
}
var_export($result);
Input/Output:
$array
$result
[['b', 'e', 'd', 's'], ['l', 'n']]
['b', 'l', 'e', 'n', 'd', 's']
['f', 'g', 'n', 's'], ['r', 'm'], ['a', 'e', 't']
['f', 'r', 'a', 'g', 'm', 'e', 'n', 't' 's']
The above technique is perfectly capable of accommodating 3 or more input arrays as well.
p.s. For anyone running into technical limitations because their php version, this will do the same:
$maxCount = max(array_map('count', $array));
$result = [];
for ($i = 0; $i < $maxCount; ++$i) {
foreach (array_column($array, $i) as $found) {
$result[] = $found;
}
}
...if your php version doesn't accommodate the above snippet, you really, really need to upgrade your php version (sorry, not sorry).
To avoid the counting to determine the longest subarray, you can instead transpose the data with nested loops then flatten that result structure. (Demo)
$result = [];
foreach ($array as $i => $row) {
foreach ($row as $k => $v) {
$result[$k][$i] = $v;
}
}
var_export(array_merge(...$result));
I would like to order people in balanced groups, but these people belong to a team. I want to know if it is possible to generate groups, but prevent more people from the same team are in the same group.
Example array (people => team)
$total = array(
'Kitten01' => 'A',
'Kitten02' => 'A',
'Kitten03' => 'U',
'Kitten04' => 'U',
'Kitten05' => 'B',
'Kitten06' => 'B',
'Kitten07' => 'M',
'Kitten08' => 'M',
'Kitten09' => 'C',
'Kitten10' => 'C',
'Kitten11' => 'Y',
);
I've used this function:
function partition( $list, $p ) {
$listlen = count( $list );
$partlen = floor( $listlen / $p );
$partrem = $listlen % $p;
$partition = array();
$mark = 0;
for ($px = 0; $px < $p; $px++) {
$incr = ($px < $partrem) ? $partlen + 1 : $partlen;
$partition[$px] = array_slice( $list, $mark, $incr );
$mark += $incr;
}
return $partition;
}
And finally I generate groups:
$max_group = 8;
$people = count($total);
$groups = ceil($people / $max_group);
print_r(partition($total, $groups)) ;
.. and returns this:
Array
(
[0] => Array
(
[Kitten01] => A
[Kitten02] => A
[Kitten03] => U
[Kitten04] => U
[Kitten05] => B
[Kitten06] => B
)
[1] => Array
(
[Kitten07] => M
[Kitten08] => M
[Kitten09] => C
[Kitten10] => C
[Kitten11] => Y
)
)
Is possible to return this?:
Array
(
[0] => Array
(
[Kitten01] => A
[Kitten03] => U
[Kitten05] => B
[Kitten07] => M
[Kitten09] => C
[Kitten11] => Y
)
[1] => Array
(
[Kitten02] => A
[Kitten04] => U
[Kitten06] => B
[Kitten08] => M
[Kitten10] => C
)
)
I hope your help
** Edit: Solution **
With the William's function flip(), I classify all items per team.
with the make_group() function, I take only one Kitten per team and I create a group, the next time that use the make_group() function, I take the rest.
Finally groups are ordered sequentially by team G1(A,U,B,M,C,...), G2(A,U,B,M,C,...) ...
If I merge all these groups and I divide proportionally (if exceeds the maximum permitted per group: 8) I'll never have two people in the same team:
** Final Code **
# groups quantity
$max_group = 8;
$people = count($total);
$groups = ceil($people / $max_group);
$total = flip($total);
$total_group = array();
# merge groups
for($y=0;$y<$groups;$y++)
{
$total_group = array_merge($total_group, make_group($total));
}
# .. and divide proportionally
print_r(partition($total_group, $groups));
The following should work, I believe:
// Your input array
$total = array(
'Kitten01' => 'A',
'Kitten02' => 'A',
'Kitten03' => 'U',
'Kitten04' => 'U',
'Kitten05' => 'B',
'Kitten06' => 'B',
'Kitten07' => 'M',
'Kitten08' => 'M',
'Kitten09' => 'C',
'Kitten10' => 'C',
'Kitten11' => 'Y',
);
// Helper function that flips values and keys, so that your array
// would look like:
// array(
// 'A' => array('Kitten01', 'Kitten02'),
// 'U' => array('Kitten03', 'Kitten04'),
// 'B' => array('Kitten05', 'Kitten06'),
// ...
function flip($array) {
$result = array();
foreach ($array as $k => $v) {
$result[$v][] = $k;
}
return $result;
}
// Make a group from a set of remaining members in teams
function make_group(&$teams) {
$group = array();
// Pick one member per team
foreach ($teams as $k => &$v) {
// If that team still has members, remove the member from the
// team and add it to the group
if ($member = array_shift($v)) {
$group[$member] = $k;
}
}
return $group;
}
$teams = flip($total);
// Repeat as needed
print_r(make_group($teams));
print_r(make_group($teams));
I want to sort arrays by key in php, but the alphabet that I'm using is not the normal English alphabet -- it's a self-created alphabet. Is this possible?
My alphabet is:
$alphabet = "AjawbpfmnrhHxXsSqkgtTdD =";
The array is like this:
Array (
[=k_0] => Array(
[0] => DI.3,2 &dwA-nTr& #Hrw#
[1] => mA
[2] => =k
[3] => Sfj,t
[4] => =k
[5] => pXr
)
[aA_2] => Array(
[0] => DI.7,4 &dwA-nTr& #Hrw-smA-tA,wj#
[1] => snD
[2] => aA
[3] => Sfj,t
[4] => jt
[5] => jt,w
)
[sqA_1] => Array(
[0] => DI.6,18 &dwA-nTr& #nswt#
[1] => ra
[2] => sqA
[3] => Sfj,t
[4] => =s
[5] => r
)
);
So if I sort this array following my alphabet then the array with the key [=k_0] should be at the end.
You can use the usort() function and provide your own sorting logic.
See php.net for an example.
Edit: use uksort, not usort. See http://www.php.net/manual/en/function.uksort.php. Thanks #Darien!
A slightly modified example from php.net - the original code with an $alphabet mapping added:
function cmp($a, $b)
{
// custom sort order - just swapps 2 and 3.
$alphabet = array (1 => 1, 2 => 3, 3 => 2, 4 => 4, 5 => 5, 6=> 6);
if ($alphabet[$a] == $alphabet[$b]) {
return 0;
}
return ($alphabet[$a] < $alphabet[$b]) ? -1 : 1;
}
$a = array(3 => 'c' , 2 => 'b', 5 => 'e', 6 => 'f', 1=>'a');
uksort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
Given your $alphabet = "AjawbpfmnrhHxXsSqkgtTdD";, and assuming that A<j<a, etc, per your comment, transform each key in the alternative alphabet to a series in the known alphabet, e.g. use a mapping like:
your alphabet: AjawbpfmnrhHxXsSqkgtTdD
'real'alphabet: abcdefghijklmnopqrstuvw
So the key 'Ajaw' => 'abcd', and 'fmnr' => 'ghij', etc. This then turns your keys into something you can sort using conventional php functions. You'd need some way to handle characeters not present in your original alphabet though.
Something like that might work - you'd need two transform functions (from your alphabet to 'real' alphabet and vice versa), and then a comparator for e.g. uksort.
My two cents - thanks for clarifying the original question.
Fortunately, your custom alphabet does not have more characters than the list of single-byte latin letters, so translating is a very simple and readable process.
I recommend that you set up a translation array before you begin sorting, then translate/normalize the keys for the purpose of sorting.
Code: (Demo)
$array = [
'=k_0' => ['test1'],
'aA_2' => ['test2'],
'sqA_1' => ['test3'],
'=kj_0' => ['test4'],
'awA_2' => ['test5'],
'= D_1' => ['test6'],
'sq A_1' => ['test7'],
'sqA_2' => ['test8'],
];
$trans = ['AjawbpfmnrhHxXsSqkgtTdD =', 'abcdefghijklmnopqrstuvwxy'];
uksort(
$array,
function ($a, $b) use ($trans) {
return strtr($a, ...$trans) <=> strtr($b, ...$trans);
}
);
var_export($array);
Output:
array (
'aA_2' =>
array (
0 => 'test2',
),
'awA_2' =>
array (
0 => 'test5',
),
'sqA_1' =>
array (
0 => 'test3',
),
'sqA_2' =>
array (
0 => 'test8',
),
'sq A_1' =>
array (
0 => 'test7',
),
'=k_0' =>
array (
0 => 'test1',
),
'=kj_0' =>
array (
0 => 'test4',
),
'= D_1' =>
array (
0 => 'test6',
),
)
From PHP7.4, the syntax can be reduced using arrow function syntax.
uksort(
$array,
fn($a, $b) => strtr($a, ...$trans) <=> strtr($b, ...$trans)
);
After feedback from #mickmackusa I have updated my example to work with uksrot and answer the question fully
$order = str_split("AjawbpfmnrhHxXsSqkgtTdD");
uksort($arr, function ($a, $b) use ($order) {
$posA = array_search($a, $order);
$posB = array_search($b, $order);
return $posA - $posB;
});
http://sandbox.onlinephpfunctions.com/code/9b6f39b30dcc932517bbe82608dd8a0c8d35b3da
-- original response with usort--
You can use usort() with a custom order array like so
$arr = array("w","b","m","n","x","x","z","T","T","A","A");
$order = array("A","j","a","w","b","p","f","m","n","r","h","H","x","X","s","S","q","k","g","t","T","d","D"," ","=");
usort($arr, function ($a, $b) use ($order) {
$posA = array_search($a, $order);
$posB = array_search($b, $order);
return $posA - $posB;
});
(you could probably just explode the $order string so it's nicer)
I actually just came across this answer which explains it better, and handles if a value is not inside the order array.
And a working example http://sandbox.onlinephpfunctions.com/code/3934aafe93377ec18549d326d6551608436242a7
<?php
$arr = [8,10,12,18,20,7,4,6,2,20,0]; //take array
$a= sortasc($arr); // call function
function sortasc($arr){
for($i=0;$i<=count($arr);$i++){
for($j=1;$j<=count($arr)-1;$j++){
if($arr[$j-1]>$arr[$j]){
$temp = $arr[$j];
$arr[$j]= $arr[$j-1];
$arr[$j-1] = $temp;
}
}
}
return $arr;
}
?>
See this code:
<?php
$arr = array('wr' => 1, 'wrS' => 6, 'wr,w' => 3, 'wr.w' => 4, 'wr-qA' => 2, 'wrs' => 5);
function compare_by_alphabet(array $alphabet, $str1, $str2)
{
$l1 = strlen($str1);
$l2 = strlen($str2);
$c = min($l1, $l2);
for ($i = 0; $i < $c; $i++)
{
$s1 = $str1[$i];
$s2 = $str2[$i];
if ($s1===$s2) continue;
$i1 = array_search($s1, $alphabet);
if ($i1===false) continue;
$i2 = array_search($s2, $alphabet);
if ($i2===false) continue;
if ($i2===$i1) continue;
if ($i1 < $i2) return -1;
else return 1;
}
if ($l1 < $l2) return -1;
elseif ($l1 > $l2) return 1;
return 0;
}
function compare_keys_by_alphabet($a, $b)
{
static $alphabet = array('-', ',', '.', 'A', 'j', 'a', 'w', 'b', 'p', 'f', 'm', 'n', 'r', 'h', 'H', 'x', 'X', 's', 'S', 'q', 'k', 'g', 't', 'T', 'd', 'D', '=', '/', '(', ')', '[', ']', '<', '>', '{', '}', '\'', '*', '#', 'I', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, '&', '#');
return compare_by_alphabet($alphabet, $a, $b);
}
uksort($arr, 'compare_keys_by_alphabet');
print_r($arr);
Result:
Array
(
[wr] => 1
[wr-qA] => 2
[wr,w] => 3
[wr.w] => 4
[wrs] => 5
[wrS] => 6
)