Say I have the following table:
name
----
A
B
C
D
E
F
G
H
I
I can extract this from MySQL and put it in an array:
$result = mysql_query("SELECT * FROM names ORDER BY name DESC");
while ($row = mysql_fetch_object($result)) {
$names[] = $row->name
}
What I am now looking for is a script that loops through $names and returns pairs, for instance every 3rd name:
array(
[A] => [C],
[B] => [D],
[C] => [E],
[D] => [F]
[E] => [G]
[F] => [H]
[G] => [I]
)
Or, for instance, every 4th name:
array(
[A] => [D],
[B] => [E],
[C] => [F],
[D] => [G]
[E] => [H]
[F] => [I]
)
The number of names in between (3 in the fist example, 4 in the second) should be variable. Does anybody know how to do this in php?
You can do this using a fairly simple for loop.
Here's an example. All you need to do is tweak $seperation:
<?php
$array = array('A','B','C','D','E','F','G','H','I');
$seperation = 3;
$assoc = array(); // stores result
for($i = 0; $i+$seperation < count($array); $i++) {
$assoc[$array[$i]] = $array[$i + $seperation];
}
print_r($assoc);
Demo
Another option is to use the array_slice() and array_combine() functions to create the key/value pairs that you are wanting.
function array_nth(array $array, $nth)
{
$keys = array_slice($array, 0, -($nth - 1));
$values = array_slice($array, $nth - 1);
return array_combine($keys, $values);
}
For example,
$names = range('A', 'I');
var_export(array_nth($names, 3));
Gives
array (
'A' => 'C',
'B' => 'D',
'C' => 'E',
'D' => 'F',
'E' => 'G',
'F' => 'H',
'G' => 'I',
)
I'm assuming these are indexed (by number)? Since in your example you just list [a, b, c]. Something like (might have an off by one bug but this is the idea):
function pairs($arr, $offset) {
$rv = array();
for ($i = 0; $i < (sizeof($arr) - $offset); $i++) {
$rv[$arr[$i]] = $arr[$i + $offset];
}
return $rv;
}
Think it's pretty self explanatory. (sizeof($arr) - $offset) just makes sure you don't go off the end whne you have the $arr[$i + $offset];
Related
I am trying to reduce an associative array to pairs of unique values, whereby the keys are letters to be paired and the values are their count()s.
Each pair cannot contain the same letter twice such as AA or BB.
It is permissible for more than one occurrence of the same pair.
e.g. AC, DC, AC, DC are all valid in the resultant array
Every time a letter is picked, its associated number decreases by one until none remain, or
any odd/leftover letters should be flagged and not ignored, e.g. Array([paired]=>Array(...) [unpaired]=>Array(...))
Sample input:
Array(
[A] => 8
[B] => 16
[C] => 15
[D] => 4
[E] => 1
)
Possible output:
[
'paired' => [
'BD', 'AE', 'BC', 'AC', 'BC', ...
],
'unpaired' => [
'C' => 4
]
]
I would prefer two unique letters to be chosen at random, but if that is too hard then the resultant array should be easily shuffle()-able
I have tried various combinations of array_reduce(), array_map(), array_walk(), array_unique() etc., even Pear's Math_Combinatorics, but all to no avail.
Here is a function that will generate the results you want. It iterates over the letters array, filtering out letters which have been completely used, until there is only one letter which is unused. The list of pairs and the unused letter are then returned in an array:
function random_pairs($letters) {
$pairs = array();
$letters = array_filter($letters);
while (count($letters) > 1) {
$keys = array_keys($letters);
shuffle($keys);
list($letter1, $letter2) = array_slice($keys, 0, 2);
$pairs[] = "$letter1$letter2";
$letters[$letter1]--;
$letters[$letter2]--;
$letters = array_filter($letters);
}
return array('pairs' => $pairs, 'unpaired' => $letters);
}
Sample usage:
$letters = array('A' => 8, 'B' => 16, 'C' => 15, 'D' => 4, 'E' => 1);
print_r(random_pairs($letters));
Sample output:
Array
(
[pairs] => Array
(
[0] => CB
[1] => CE
[2] => CD
[3] => BD
[4] => CB
[5] => DC
[6] => AB
[7] => CA
[8] => DA
[9] => BC
[10] => BA
[11] => BA
[12] => AB
[13] => BA
[14] => BC
[15] => AB
[16] => BC
[17] => CB
[18] => CB
[19] => CB
[20] => CB
)
[unpaired] => Array
(
[C] => 2
)
)
Demo on 3v4l.org
Here is a recursive technique. It uses array_rand() to grab two unique keys at a time. If you need the paired keys to be randomly arranged, then call shuffle() upon them. What I mean is, shuffle() might be omittable depending on your expectations.
An advantage in using array_rand() is that array_keys() doesn't need to be called since it returns random keys.
I have deliberately designed this snippet to unconditionally declare the unpaired subarray -- even if it is empty.
Code: (Demo)
function randomPairs(array $letters, array $result = []) {
if (count($letters) < 2) {
$result['unpaired'] = $letters;
return $result;
}
$keys = array_rand($letters, 2);
shuffle($keys);
$result['paired'][] = $keys[0] . $keys[1];
--$letters[$keys[0]];
--$letters[$keys[1]];
return randomPairs(array_filter($letters), $result);
}
Without recursion: (Demo)
function randomPairs(array $letters): array {
$result['paired'] = [];
while (count($letters) > 1) {
$keys = array_rand($letters, 2);
shuffle($keys);
$result['paired'][] = $keys[0] . $keys[1];
--$letters[$keys[0]];
--$letters[$keys[1]];
$letters = array_filter($letters);
}
$result['unpaired'] = $letters;
return $result;
}
I propose you this one :
I've just seen Nick's answer, but it do 15 minutes i'm trying xD I want to post my answer :p
Based on a "pickRandomValues" function, i made it dynamic, and you can as well choose the number of letters you want
function pickRandom(&$array = [], $count = 1) {
$valuesFound = [];
if (count($array) >= $count) {
for ($i = 0; $i < $count; $i++) {
$index = rand(0, count($array) - 1);
$tmp = array_splice($array, $index, 1);
$valuesFound[] = $tmp[0];
}
}
return $valuesFound;
}
And you body code
$tmpArr = [];
foreach ($a as $letter => $count) {
$t = [];
$tmpArr = array_merge($tmpArr, array_pad($t, $count, $letter));
}
$pairs = [];
while (count($tmpArr) >= $numberOfLetters) {
$pick = pickRandom($tmpArr, $numberOfLetters);
$pairs[] = implode($pick);
}
$finalArray = [
"paired" => $pairs,
"unpaired" => array_count_values($tmpArr)
];
So, it give something like this :
http://sandbox.onlinephpfunctions.com/code/3b143396b7267bd6029ff613746d6c047557a282
How can I reference the following dynamic arrays' elements ?
$log = array();
$arr1 = array ('a'=>'6:16pm','b'=>2,'c'=>3,'d'=>4,'e'=>5);
$arr2 = array ('a'=>'6:24pm','b'=>20,'c'=>30,'d'=>40,'e'=>50);
$log = array_merge($log, array($arr1['a']=>$arr1));
$log = array_merge($log, array($arr2['a']=>$arr2)); //<-- to use time as key
print_r($log);
for ($x = 0; $x < count($log); $x++) {
print_r ($log[0][$x]['a']); // <-- referencing issue Undefined offset: 0 .. line 20
}
//------ produces
Array
(
[6:16pm] => Array
(
[a] => 6:16pm
[b] => 2
[c] => 3
[d] => 4
[e] => 5
)
[6:24pm] => Array
(
[a] => 6:24pm
[b] => 20
[c] => 30
[d] => 40
[e] => 50
)
)
I'm pretty sure it has something to do with the way I'm naming the $log main array.. and there's probably a better way to do what I wish(.. add/ new elements to $log using the time key) - Still a php noob unfortunately. Thanks for any pointers.
It's a little unclear, but just create an array from the two and use the a index:
$log = array($arr1, $arr2);
foreach($log as $values) {
echo $values['a']; // 6:16pm
}
Or if you want the time as the index, then re-index on a:
$log = array($arr1, $arr2);
$log = array_column($log, null, 'a');
foreach($log as $time => $values) {
echo $time; // 6:16pm
echo $values['b']; // 2
}
It's prettier, but there is no need for the time as the index unless you are going to use ksort or access by index:
echo $log['6:16pm']['b'];
in cases that you don't know your key is recommended that you use foreach statement:
$logs = array();
$arr1 = array ('a'=>'6:16pm','b'=>2,'c'=>3,'d'=>4,'e'=>5);
$arr2 = array ('a'=>'6:24pm','b'=>20,'c'=>30,'d'=>40,'e'=>50);
$logs = array_merge($logs, array($arr1['a']=>$arr1));
$logs = array_merge($logs, array($arr2['a']=>$arr2)); //<-- to use time as key
$logs = array();
$arr1 = array('a' => '6:16pm', 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);
$arr2 = array('a' => '6:24pm', 'b' => 20, 'c' => 30, 'd' => 40, 'e' => 50);
$logs = array_merge($logs, array($arr1['a'] => $arr1));
$logs = array_merge($logs, array($arr2['a'] => $arr2)); //<-- to use time as key
foreach ($logs as $time => $log) {
//index:
print_r($time);
//array:
print_r($log);
// a array key:
print_r($log['a']);
//go through all keys:
foreach ($log as $letter => $value) {
//index:
print_r($letter);
//value: a
print_r($value);
}
}
You can not call array like
print_r ($log[0]);
Because your array has key. which is 6:16pm for first one and 6:24pm for second. you have to call it by key name you have assigned. your array should be called like this in everywhere even in loop
print_r ($log["6:16pm"]);
I'm running a foreach loop on an array of students, that have a key ['all_user_grades']. What is the best way to count the number of As,Bs,Cs,Ds,Es and fails for each student in the array.
Here's what my array looks like:
[11] => Array
(
[id] => 10
[All_User_Grades] => A, A, D, A, E
)
Here's what my foreach looks like so far:
foreach($user_grades as $k => $v){
$aug = $v['All_User_Grades'];
$all_user_grades_arr = explode(',', $aug);
}
You can use the array_count_values() function:
$array = array('A', 'A', 'D', 'A', 'E');
$result = array_count_values($array);
print_r($result);
It outputs:
Array
(
[A] => 3
[D] => 1
[E] => 1
)
You can use substr_count() for this like so
$As = substr_count($aug, 'A');
$Bs = substr_count($aug, 'B');
//etc
or, just like you already did, explode and use the array for calculation
$all_user_grades_arr = explode(',', $aug);
$grades = array('A' => 0, 'B' => 0', ...);
foreach ($all_user_grades_arr as $val) {
$grades[ trim($val) ]++;
}
The trim() here is necessary to get rid of unnecessary whitespaces
You can try this way using array_count_values, i have used for single array, you change as per your requirements. See Demo http://ideone.com/YrGfPD
//PHP
$v=array('Id'=>10,'All_User_Grades'=>'A,A,D,A,E');
$aug = $v['All_User_Grades'];
$all_user_grades_arr = explode(',', $aug);
echo '<pre>';
print_r(array_count_values($all_user_grades_arr));
echo '</pre>';
//OUTPUT
Success time: 0.02 memory: 24448 signal:0
Array
(
[A] => 3
[D] => 1
[E] => 1
)
$array = array(
'2' => 'a',
'5' => 'b',
'1' => 'c',
'5' => 'd',
'3' => 'e'
)
foreach($array as $key => $value){
$array1 = $array;
foreach($array1 as $key1 => $value1){
if($key > $key1){
$result[$key1] = $value1;
}
}
}
print_r($result);
this is my output:
Array
(
[1] => c
[2] => a
[3] => e
)
i am making comparison of key with same key by storing this array in another array,
if num > num in this case the maximum number 5(5>5) this condtions fails so 5 is not in the new array. so please can anyone tell me how this will sort or is there any better way.
thanks in advance.
Your algorithm does not work because the outer loop iterate over each key, and inner loop will try to insert any keys less than the current key into the array. Although these keys are smaller than the current key, but it does not guarantee they are in ascending order. For example, the following array will not work:
array(
3 => 'a',
2 => 'b',
1 => 'c'
);
Of course it has the issue of missing some element from the original array as you may have already noticed.
Instead you can use any sorting algorithm (like mergesort, quicksort, etc) to sort the keys first, then build the new associative array. Below implements insertion_sort (because it is easy to do).
function insertion_sort($arr)
{
for ($i = 0; $i < count($arr); $i++) {
$j = $i;
while ($j > 0 && $arr[$j] < $arr[$j-1]) {
$tmp = $arr[$j-1];
$arr[$j-1] = $arr[$j];
$arr[$j] = $tmp;
$j--;
}
}
return $arr;
}
$array = array(
'2' => 'a',
'5' => 'b',
'1' => 'c',
'5' => 'd',
'3' => 'e'
);
$keys = array_keys($array);
$sorted = array();
foreach (insertion_sort($keys) as $key) {
$sorted[$key] = $array[$key];
}
print_r($sorted);
prints
Array
(
[1] => c
[2] => a
[3] => e
[5] => d
)
It should quite simple algorithm, but I just can't get around it.
I have some arrays in alphabetical order
[0] => Array
(
[0] => a
[1] => b
[2] => c
)
and for example
[0] => Array
(
[0] => a
[1] => b
[2] => c
[3] => d
)
and I need to sort them into rows. For example:
I should receive a table with 3 columns and as many rows as it may get and it should be in alphabetical order.
Here is an example:
First array should be converted into
[0] => Array
(
[0] => Array
(
[0] => a
[1] => b
[2] => c
)
)
But second one should be as
[1] => Array
(
[0] => Array
(
[0] => a
[1] => c
[2] => d
)
[1] => Array
(
[0] => b
)
)
I'm writing it in php, so if anyone can help I would be really appreciated.
UPD:
Code example:
function sortAsOrdered( array $categories )
{
foreach ( $categories as $groupId => $group )
{
$regroupMenuItems = array();
$limit = count( $group );
$rows = ceil( $limit / 3 );
for ( $i = 0; $i < $rows; ++$i )
{
$jumper = 0;
for ( $j = 0; $j < 3; $j++ )
{
if ( 0 == $jumper )
{
$jumper = $i;
}
if ( isset( $group[ $jumper ] ) )
{
$regroupMenuItems[ $i ][ $j ] = $group[ $jumper ];
}
$jumper = $jumper + $rows;
}
}
$categories[ $groupId ] = $regroupMenuItems;
}
return $categories;
}
Guys I solved this one. Here you could see my algorithm http://pastebin.com/xe2yjhYW.
But don't be sad your help will not go in vain. I probably will place bounty just for those who helped with this dificult algorithm for me.
Guys thanks one more time. Your thoughts inspired me to think differently.
array_chunk() wold have been the solution but as you want it to be specially sorted, that wouldn't help you much.
So here is my five cents:
function array_chunk_vertical($input, $size_max) {
$chunks = array();
$chunk_count = ceil(count($input) / $size_max);
$chunk_index = 0;
foreach ($input as $key => $value) {
$chunks[$chunk_index][$key] = $value;
if (++$chunk_index == $chunk_count) {
$chunk_index = 0;
}
}
return $chunks;
}
$array = array('a', 'b', 'c', 'd', 'e', 'f');
var_dump(array_chunk_vertical($array, 2));
Which will give you:
array
0 =>
array
0 => string 'a' (length=1)
3 => string 'd' (length=1)
1 =>
array
1 => string 'b' (length=1)
4 => string 'e' (length=1)
2 =>
array
2 => string 'c' (length=1)
5 => string 'f' (length=1)
The downside of this function is that you can only tell the max number of elements in a chunk, and then it equally divides the array to chunks. So for [4] and max_size 3 you will get [2,2] unlike the expected [3,1].
<?php
$five_el = array('a', 'b', 'c', 'd', 'e');
$two_el = array('a', 'b');
$three_el = array('a', 'b', 'c');
$six_el = array('a', 'b', 'c', 'd', 'e', 'f');
function multid($sorted_array) {
$mulidarray = array();
$row = 0;
$column = 0;
foreach ($sorted_array as $value) {
if ($column == 3) {
$row++;
}
$column++;
if (!isset($mulidarray[$row])) {
$mulidarray[$row] = array();
}
$multidarray[$row][] = $value;
}
return $multidarray;
}
var_dump(multid($five_el));
var_dump(multid($two_el));
var_dump(multid($three_el));
var_dump(multid($six_el));
array_chunk is a natural first approach to the problem, but it won't do exactly what you need to. If the solution is provided that way, you need to either restructure the resulting array or restructure the input before processing it, as below:
$input = range('a', 'k'); // arbitrary
$columns = 3; // configure this
$rows = ceil(count($input) / $columns);
// fugly, but this way it works without declaring a function
// and also in PHP < 5.3 (on 5.3 you'd use a lambda instead)
$order = create_function('$i',
'$row = (int)($i / '.$rows.');'.
'$col = $i % '.$rows.';'.
'return $col * ('.$columns.' + 1) + $row;');
// $order is designed to get the index of an item in the original array,
// and produce the index that item would have if the items appeared in
// column-major order instead of row-major as they appear now
$array = array_map($order, array_keys($input));
// replace the old keys with the new ones
$array = array_combine($array, $input);
// sort based on the new keys; this will effectively transpose the matrix,
// if it were already structured as a matrix instead of a single-dimensional array
ksort($array);
// done!
$array = array_chunk($array, $columns);
print_r($array);
See it in action.
Let's see if this is nearer the mark
function splitVerticalArrayIntoColumns($aInput, $iNumberOfColumns) {
//output array
$aOutput = array();
//the total length of the input array
$iInputLength = count($aInput);
//the number of rows will be ceil($iInputLength / $iNumberOfColumns)
$iNumRows = ceil($iInputLength / $iNumberOfColumns);
for($iInputIndex = 0; $iInputIndex < $iInputLength; $iInputIndex++) {
$iCurrentRow = $iInputIndex % $iNumRows;
$aOutput[$iCurrentRow][] = $aInput[$iInputIndex];
}
//return
return $aOutput;
}
Which - when run thus:
$aList = array("a", "e", "d", "b", "c");
echo 'array("a", "e", "d", "b", "c")' . "\n\n";
print_r(splitVerticalArrayIntoColumns($aList, 3));
Gives:
array("a", "e", "d", "b", "c")
Array
(
[0] => Array
(
[0] => a
[1] => d
[2] => c
)
[1] => Array
(
[0] => e
[1] => b
)
)
That's not sorting each row yet but is that the kind of thing you're after?
begin facepalm edit
... or of course, array_chunk($aList, 3) after you've sorted it O_o
http://uk3.php.net/manual/en/function.array-chunk.php
I'll leave everything below for reference or whatever - I'd completely forgotten about array_chunk()
end facepalm edit
I'd use a modulo in a loop where you're counting the array index (after sorting the array) - for instance if you're trying to split an array into 3 "columns" you could try something like:
if($iIndex % 3 == 0) {
//... create a new array
}
else {
//... add to an existing array
}
EDIT code example:
$aList = array("a", "e", "d", "b", "c");
sort($aList);
$iDesiredNumberOfColumns = 3;
$iListLength = count($aList);
$aListInColumns = array();
$iRowNumber = 0;
for($iIndex = 0; $iIndex < $iListLength; $iIndex++) {
$iColumnNumber = $iIndex % 3;
if($iIndex != 0 && $iColumnNumber == 0) {
$iRowNumber++;
}
$aListInColumns[$iRowNumber][$iColumnNumber] = $aList[$iIndex];
}
Just ran it on my local server (and corrected the typo), and it outputs as:
Array
(
[0] => Array
(
[0] => a
[1] => b
[2] => c
)
[1] => Array
(
[0] => d
[1] => e
)
)
There's probably a tidier way of doing it (that's a little procedural) but it should do the job.
How about:
$arrs = array(
array('a','b','c'),
array('a','b','c','d'),
array('a','b','c','d','e'),
array('a','b','c','d','e','f'),
array('a','b','c','d','e','f','g')
);
$nbcols = 3;
foreach ($arrs as $arr) {
$arr_size = count($arr);
$nblines = ceil($arr_size/$nbcols);
$res = array();
$l = 0;
foreach ($arr as $el) {
if ($l == $arr_size - 1 && count($res[0]) < $nbcols) $l=0;
$res[$l%$nblines][] = $el;
$l++;
}
print_r($res);
}
output:
Array
(
[0] => Array
(
[0] => a
[1] => b
[2] => c
)
)
Array
(
[0] => Array
(
[0] => a
[1] => c
[2] => d
)
[1] => Array
(
[0] => b
)
)
Array
(
[0] => Array
(
[0] => a
[1] => c
[2] => e
)
[1] => Array
(
[0] => b
[1] => d
)
)
Array
(
[0] => Array
(
[0] => a
[1] => c
[2] => e
)
[1] => Array
(
[0] => b
[1] => d
[2] => f
)
)
Array
(
[0] => Array
(
[0] => a
[1] => d
[2] => g
)
[1] => Array
(
[0] => b
[1] => e
)
[2] => Array
(
[0] => c
[1] => f
)
)
In order to do this, you need to do two operations:
First, split the array into 3 groups, as evenly as possible.
function array_grouped($arr, $group_count)
{
if (!count($arr)) return array();
$result = array();
for ($i = $group_count; $i > 0; --$i)
{
# break off the next ceil(remaining count / remaining columns) elements
# (avoiding FP math, cause that way lies madness)
$result[] = array_splice($arr, 0, ((count($arr)-1) / $i) + 1);
}
return $result;
}
Then, "transpose" the array, so that rows and columns switch places.
function array_transposed($arr)
{
$result = array();
foreach ($arr as $x => $subarr)
{
foreach ($subarr as $y => $val)
{
if (!isset($result[$y])) $result[$y] = array();
$result[$y][$x] = $val;
}
}
return $result;
}
array_transposed(array_grouped($arr, 3)) gives you entries in the order you want them.
YAYAYAY!! I've got it. You could turn this into a function if you'll be doing it regularly.
# Here we setup our array and the number of columns we want.
$myArray = range('a','d');
$numCols = 3;
# Here we break ourselves up into columns
for ($i = 0; $i < $numCols; $i++) {
$numRows = ceil(count($myArray) / ($numCols - $i));
$columns[$i] = array_slice($myArray,0,$numRows);
$myArray = array_slice($myArray,$numRows);
}
# Here we transpose our array to be in rows instead of columns.
for ($i = 0; $i < $numCols; $i++) {
for ($j = 0; $j < count($columns[$i]); $j++) {
$rows[$j][$i] = $columns[$i][$j];
}
}
# Our rows are now in $rows
var_dump($rows);
The output from this is:
array(2) {
[0]=>
array(3) {
[0]=>
string(1) "a"
[1]=>
string(1) "c"
[2]=>
string(1) "d"
}
[1]=>
array(1) {
[0]=>
string(1) "b"
}
}
If to say it shortly, then here is a method for that algorithm.
/**
* #param array $toTransform
* #param int $columnsMax
* #return array
*/
private function transformation( array $toTransform, $columnsMax = 3 )
{
// First divide array as you need
$listlen = count( $toTransform );
$partlen = floor( $listlen / $columnsMax );
$partrem = $listlen % $columnsMax;
$partition = array();
$mark = 0;
for ( $px = 0; $px < $columnsMax; $px++ )
{
$incr = ( $px < $partrem ) ? $partlen + 1 : $partlen;
$partition[ $px ] = array_slice( $toTransform, $mark, $incr );
$mark += $incr;
}
// Secondly fill empty slots for easy template use
$result = array();
for ( $i = 0; $i < count( $partition[0] ); $i++ )
{
$tmp = array();
foreach ( $partition as $column )
{
if ( isset( $column[ $i ] ) )
{
$tmp[] = $column[ $i ];
}
else
{
$tmp[] = '';
}
}
$result[] = $tmp;
}
return $result;
}
Also I included PHPUnit test for that. You can find it at, that link.