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'm trying to create a script to create pairs from 2 different arrays. For example I have two array given below:
<?php
//Array 1
$arr1 = array('A', 'B', 'C', 'D');
//Array 2
$arr2 = array('X', 'Y', 'Z');
And I need output in such a manner so that it match with each element and didn't repeat pair and sequence also. Here is expected output:
$output = array(
0 => array('A', 'X'),
1 => array('B', 'Y'),
2 => array('C', 'Z'),
3 => array('D', 'X'),
4 => array('A', 'Y'),
5 => array('B', 'Z'),
6 => array('C', 'X'),
7 => array('D', 'Y'),
8 => array('A', 'Z'),
9 => array('B', 'X'),
10 => array('C', 'Y'),
11 => array('D', 'Z')
)
**Note: 2 arrays can differ with number of count and values.
This is Cartesian Product. Could be achieved with
//Array 1
$arr1 = array('A', 'B', 'C', 'D');
//Array 2
$arr2 = array('X', 'Y', 'Z');
$output = array();
foreach($arr1 as $i1){
foreach ($arr2 as $i2) {
$output[] = array($i1, $i2);
}
}
echo json_encode($output);
If you want the output to be in the same order as you have mentioned above, following should do that.
$newoutput = array();
$x = 0;
$y = 0;
for($i = 1; $i <= (count($arr1) * count($arr2)); $i++){
$newoutput[] = array($arr1[($x % count($arr1))], $arr2[($y % count($arr2))]);
$x++;
$y++;
}
echo "<br />", json_encode($newoutput);
Output however, is a different order of the same output as previous.
EDIT
The second works only when (count($arrX) % count($arrY)) > 0.
I don't think there is a built in support for that.
You need to loop the arrays nested and build a result array.
$arr1 = array('A', 'B', 'C', 'D');
$arr2 = array('X', 'Y', 'Z');
foreach($arr1 as $item1){
foreach($arr2 as $item2){
$res[] = [$item1, $item2];
}
}
var_dump($res);
https://3v4l.org/CdE8Q
Try This out (This will sort out the same pair if found):
//Array 1
$arr1 = array('A', 'B', 'C', 'D', 'A', 'E'); // Same element will be sort out in pair
//Array 2
$arr2 = array('X', 'Y', 'Z' , 'X', 'Y', 'X'); // Same element will be sort out in pair
$output = array();
$used_pair = array(); // Keeping used pair
$i = 1;
foreach ($arr1 as $each1) {
foreach ($arr2 as $each2) {
if (!in_array($each1.$each2, $used_pair)) {
$output[] = array($each1, $each2);
$used_pair[] = $each1 . $each2;
}
}
}
print_r($used_pair); // array with pair string
print_r($output); // array with pair array
this is an example that ouputs unique values in the same order :
online eval : https://3v4l.org/H2pad
<?php
function cartesian(array $x,array $y): array
{
$result = [];
$size = count($x) * count($y);
$posX = 0;
$posY = 0;
for($i = 0; $i < $size; $i++) {
if (!isset($x[$posX])) {
$posX = 0;
} elseif(!isset($y[$posY])) {
$posY = 0;
}
$result[] = [$x[$posX],$y[$posY]];
$posX++; $posY++;
}
return $result;
}
$x = array('A', 'B', 'C', 'D');
$y = array('X', 'Y', 'Z');
print_r(cartesian($x,$y));
( tbh, it took me a lot of rubber duck debugging )
In a word game with 2 players I would like to give 7 random letters to each player in the PHP 5.3 function creating a new game:
$LETTERS = array( '*', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' );
$NUMBERS = array( 2, 10, 3, 5, 3, 5, 9, 2, 2, 8, 4, 6, 4, 5, 8, 10, 6, 6, 6, 5, 3, 1, 2, 1, 2, 1, 1 );
function createGame($dbh, $uid) {
global $LETTERS, $NUMBERS;
$letters = array();
$letters1 = array();
$letters2 = array();
for ($i = 0; $i < count($LETTERS); $i++) {
$letter = $LETTERS[$i];
$number = $NUMBERS[$i];
for ($n = 0; $n < $number; $n++) {
array_push($letters, $letter);
}
}
for ($i = 0; $i < 7; $i++) {
/* TODO move random letter from $letters to $letter1 */
/* TODO move random letter from $letters to $letter2 */
}
$sth = $dbh->prepare('insert into games (player1, stamp1, stamp2, letters1, letters2, letters, board) values (?, unix_timestamp(now()), 0, ?, ?, ?, space(225))');
$sth->execute(array($uid, join('', $letters1), join('', $letters2), join('', $letters)));
return $dbh->lastInsertId();
}
I know that array_rand() can be used to select a random element from an array.
But how to use it here in a most effective way to move a letter from the $letters array?
UPDATE:
I've tried the following, but the first 7 letters are not removed from the beginning of the source array, why?
shuffle($letters);
$letters1 = array_slice($letters, 0, 7);
$letters2 = array_slice($letters, 0, 7);
I would shuffle the array of letters and remove the first element of the shuffeld array to get one random letter.
shuffle($letters); // Mix alle array elements
for ($i = 0; $i < 7; $i++) {
$letters1[] = array_shift($letters); // get first array element, removing it from $letters
$letters2[] = array_shift($letters); // get first array element, removing it from $letters
}
You can use array_diff:
$letters = array_diff($letters, $letter1);
$letters = array_diff($letters, $letter2);
I have an array like :
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
[6] => g
[7] => h
)
And I want add semicolon(;) every 3 index value and it's read from end of array that result is string like "ab;cde;fgh";
Here's a fun, and kind of obnoxious, one-liner:
$str = ltrim(strrev(chunk_split(implode(array_reverse($arr)), 3, ';')), ';');
Example:
$arr = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h');
$str = ltrim(strrev(chunk_split(implode(array_reverse($arr)), 3, ';')), ';');
echo $str; //ab;cde;fgh
// More sample output based on different input arrays:
$arr = array('a', 'b', 'c', 'd', 'e'); //ab;cde
$arr = array('a', 'b', 'c', 'd', 'e', 'f'); //abc;def
$arr = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'); //ab;cde;fgh;ijk
See demo
Its an odd way, but since you want it reversed you may need to use some function here:
$array = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h');
$array = array_reverse($array); // reverse it
$array = array_chunk($array, 3); // cut by threes
$string = '';
foreach ($array as $value) {
$string .= implode($value); // glue them
if(count($value) == 3) { // if still three, add a semi
$string .= ';';
}
}
$string = strrev($string); // then reverse them again
echo $string; // ab;cde;fgh
This works... there's a couple different ways to do this. This was the quickest way off the top of my head without using a second array.
$vars = array("a","b","c","d","e","f","g","h");
print_r(insert_char($vars));
function insert_char($Array, $Delimeter=";", $Count=3) {
for ($i = sizeOf($Array) - $Count; $i > 0; $i -= $Count)
array_splice($Array, $i, 0, $Delimeter);
return implode($Array);
}
Result
ab;cde;fgh
Here's my solution, although it feels inefficient it is pretty clear:
<?php
$myarr=Array('a','b','c','d','e','f','g','h');
$newarr=Array();
$arrsize = sizeof($myarr);
for ($x = 0, $i = 0; $x < $arrsize; $x++, $i++) {
$newarr[$i] = $myarr[$arrsize - $x - 1];
if (($arrsize - $x) % 3 == 0) {
$i++;
$newarr[$i] = ";";
}
}
$newarr = array_reverse($newarr);
print_r($newarr);
?>
Just traversing it from backward and joining the string in reverse too gives that result.
$vars = array("a","b","c","d","e","f","g");
$c = 0; $s = "";
for ($i = sizeof($vars)-1; $i >= 0; $i--)
{
$c++;
$s = $vars[$i].$s;
if ($c == 3) { $c = 0; $s = ";".$s; }
}
echo $s;
Say I have an $input array, that contains something like this :
array
0 => string 'a' (length=1)
1 => string 'b' (length=1)
2 => string 'c' (length=1)
3 => string 'd' (length=1)
4 => string 'e' (length=1)
5 => string 'f' (length=1)
6 => string 'g' (length=1)
7 => string 'h' (length=1)
8 => string 'i' (length=1)
9 => string 'j' (length=1)
I want to get an $output array, that would contain this :
array
0 => string 'a' (length=1)
1 => string 'c' (length=1)
2 => string 'e' (length=1)
3 => string 'g' (length=1)
4 => string 'i' (length=1)
The $output array contains half the values that were in $input ; those that had even numbered keys in the input; the first one is kept, second one is not, third one is, and so one...
(Note: the keys are not preserved ; only the values are important)
How could I do that ? Keeping only one on two values of the array ?
I have already tried some ideas, and already have a couple different solutions :
First idea: iterate over the input array, and copy the interesting values to the output array:
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();
$nbr = count($input);
for ($i = 0 ; $i < $nbr ; $i += 2) {
$output[] = $input[$i];
}
var_dump(array_values($output));
Second idea: iterate over the array, and unset what I don't want to keep:
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = $input;
$nbr = count($input);
for ($i = 1 ; $i < $nbr ; $i += 2) {
unset($output[$i]);
}
var_dump(array_values($output));
Third idea: use a combinaison of array_flip, range, array_diff_key, ... :
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();
$keys_to_exclude = array_flip(range(1, count($input)-1, 2));
$output = array_diff_key($input, $keys_to_exclude);
var_dump(array_values($output));
Fourth idea: about the same thing, but with array_intersect_key:
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();
$keys_to_include = array_flip(range(0, count($input)-1, 2));
$output = array_intersect_key($input, $keys_to_include);
var_dump(array_values($output));
Any other idea ? Even / particularly if it sounds kinda hacky or anything ?
My goal is not to get the most efficient nor simple syntax ; it's just for fun and because I am curious, actually ^^
If the title is not using the right words to describe what I want, don't hesitate to tell ; or edit it :-)
<?php
$x = range('a', 'f');
$x = array_map('array_shift',
array_chunk($x, 2)
);
var_dump($x);
or another one
<?php
class ArrayEvenIterator extends ArrayIterator {
public function next() {
parent::next();
return parent::next();
}
}
$x = range('a', 'f');
$x = iterator_to_array(new ArrayEvenIterator( $x ), false);
var_dump($x);
or with a php 5.3 closure (which isn't better than global in this case ;-) )
<?php
$x = range('a', 'f');
$x = array_filter( $x, function($e) use(&$c) { return 0===$c++%2; });
var_dump($x);
Assuming numeric keys:
foreach ($array as $key => $value) {
if ($key % 2 != 0) {
unset($array[$key]);
}
}
EDIT
Here goes my slightly more insane solution which keeps the index continuous without re-indexing. ;o)
foreach ($array as $key => $value) {
if (!($key%2)) {
$array[$key/2] = $value;
}
}
$array = array_slice($array, 0, ceil(count($array)/2));
If you're using PHP 5.3 or later, or have the SPL extension installed (you will by default on PHP 5), you can use the FilterIterator and ArrayObject classes.
class EvenKeysFilter extends FilterIterator
{
private function iseven($keyval)
{
return (($keyval % 2) == 0);
}
public function accept()
{
$keyval = $this->getInnerIterator()->key();
return ($this->iseven($keyval));
}
}
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$inputobj = new ArrayObject($input);
$evenFilterIter = new EvenKeysFilter($inputobj->getIterator());
$output = iterator_to_array($evenFilterIter, false);
print_r($output);
(Props to VolkerK for pointing out iterator_to_array())
Which correctly outputs this:
Array
(
[0] => a
[1] => c
[2] => e
[3] => g
[4] => i
)
Not necessarily the most efficient method, but since you mentioned that wasn't necessarily a requirement...
flip, filter, then flip back.
<?php
function even($var)
{
return(!($var & 1));
}
$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$flipped = array_flip($input);
$filtered = array_filter($flipped, 'even');
$output = array_flip($filtered);
?>
I would invert the bitwise-odd check and either use array_filter() (and reindexing the result with array_values()) or use a classic foreach() loop.
Code: (Demo)
$array = range('a', 'j');
var_export(
array_values(
array_filter(
$array,
function($k) {
return !($k & 1);
},
ARRAY_FILTER_USE_KEY
)
)
);
echo "\n---\n";
$result = [];
foreach ($array as $index => $value) {
if (!($index & 1)) {
$result[] = $value;
}
}
var_export($result);
Output:
array (
0 => 'a',
1 => 'c',
2 => 'e',
3 => 'g',
4 => 'i',
)
---
array (
0 => 'a',
1 => 'c',
2 => 'e',
3 => 'g',
4 => 'i',
)
In my own projects, I prefer to keep global variable declarations to a minimum, so I'd use array_filter().
create a wrapper function
function getInput($i)
{
global $input;
return $input[$i*2];
}
The smallest and most efficient I guess.
function dropHalf($a){
$f=0;
foreach($a as $k=>$v)
if($f = ! $f)
unset($a[$k]);
return $a;
}
That's the smallest version I could think off.