Related
Let's say you have an array with numeric keys like
$ar = [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f', 6 => 'g'];
and a defined offset of 4 ($offset = 4). Now you want to slice a part of that array, starting with the offset.
$slice = array_slice($ar, $offset, null, true);
And you don't just want to keep the original keys, but actually raise them by 1, so the result would be:
Array
(
[5] => e
[6] => f
[7] => g
)
instead of
Array
(
[4] => e
[5] => f
[6] => g
)
Sure, you can loop through the array (foreach, array_walk) and reassign all keys, like:
$new_ar = [];
$raise_by = 1; // Could be any other amount
foreach ($slice as $key => $val) {
$new_ar[$key + $raise_by] = $val;
}
But is there any way to do it without the additional, external loop and (re)assigning the keys? Something like "slice the array at position x and start keys with x + 1"?
EDIT/Possible solutions:
Inspired by the comments, I see 2 possible solutions in addition to Brian's comment in How to increase by 1 all keys in an array?
Static, short and basic:
array_unshift($ar, 0);
$result = array_slice($ar, $offset + 1, null, true);
More flexible, but probably less performant:
$shift = 1;
$slice = array_slice($ar, $offset, null, true);
$ar = array_merge(range(1, $offset + $shift), array_values($slice));
$result = array_slice($ar, $offset + $shift, null, true);
Advantage is that one can shift the keys by any arbitrary value.
You can do this. If you don't want to use loop. use array_combine with empty array range from 5-7
$ar = [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f', 6 => 'g'];
$offset = 4;
$slice = array_slice($ar, $offset, null, true);
$slice = array_combine(range($offset+1, count($slice)+$offset), array_values($slice));//<--------check this
echo "<pre>";
print_r($slice);
You can get your answer by this,
$ar = [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f', 6 => 'g'];
$slice = array_slice($ar, '4', null, true);
$new_arr = array();
foreach($slice as $key => $val)
{
$key = $key + 1;
$new_arr[$key] = $val;
$key++;
}
But, check it once as #Alchalade told. Check if it do not affect your output.
Insert a new value for the original array at position 0 then slice it so each key will increase by one according to that.
$ar=array_splice($ar,0,0,' ');
$slice = array_slice($ar, $offset, null, true);
<?php
$ar = array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f', 6 => 'g');
$ar = array_combine(
array_map(function ($key) { return ++$key; }, array_keys($ar)),
$ar
);
$offset = 4;
$sliced = array_slice($ar, $offset, count($ar), TRUE);
echo '<pre>';print_r($sliced);echo '</pre>';
$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
)
I have 3 flat, indexed arrays.
$array1 = ['a', 'b', 'c', 'd'];
$array2 = ['e', 'f', 'g', 'h'];
$array3 = ['i', 'j', 'k', 'l'];
I need to merge this 3 arrays by their column position.
My expected result:
['a', 'e', 'i', 'b', 'f', 'j', 'c', 'g', 'k', 'd', 'h', 'l'];
I have already tried array_merge(), but the order of the elements does not meet my needs.
Update
My input arrays may have different lengths from one another.
$array1 = ['a', 'b', 'c', 'd', 'x', 'u', 'xx'];
$array2 = ['e', 'f', 'g', 'h', 's', 'd', 't'];
$array3 = ['i', 'j', 'k', 'l'];
If the 3 arrays have the same length, then:
$result = array();
$count = count($array1);
for ($i = 0; $i < $count; $i++)
{
$result[] = $array1[$i];
$result[] = $array2[$i];
$result[] = $array3[$i];
}
var_export($result);
-
----------------------------------
If the arrays do not have the same length:
$result = array();
$count = max(count($array1), count($array2), count($array3));
for ($i = 0; $i < $count; $i++)
{
isset($array1[$i]) && $result[] = $array1[$i];
isset($array2[$i]) && $result[] = $array2[$i];
isset($array3[$i]) && $result[] = $array3[$i];
}
var_export($result);
PHP 5.5 solution:
$array1 = array('a', 'b', 'c', 'd');
$array2 = array('e', 'f', 'g', 'h');
$array3 = array('i', 'j', 'k', 'l');
$composite = array($array1, $array2, $array3);
$count = count($array1);
$result = array();
for ($i = 0; $i < $count; $i++) {
$result = array_merge($result, array_column($composite, $i));
}
var_dump($result);
EDIT
or another alternative that will work with versions of PHP before 5.5
$array1 = array('a','b','c','d');
$array2 = array('e','f','g','h');
$array3 = array('i','j','k','l');
$composite = array($array1, $array2, $array3);
// transpose
array_unshift($composite, null);
$tmpResult = call_user_func_array('array_map', $composite);
// flatten
$result = call_user_func_array('array_merge', $tmpResult);
var_dump($result);
EDIT #2
Using this second method, if the initial arrays are varying lengths you will get "padded" NULL entries in the final result; these can be removed if they're not wanted by using array_filter();
$array1 = array('a','b','c','d','x','u','xx');
$array2 = array('e','f','g','h','s','d','t');
$array3 = array('i','j','k','l');
$composite = array($array1, $array2, $array3);
// transpose
array_unshift($composite, null);
$tmpResult = call_user_func_array('array_map', $composite);
// filter and flatten
$result = array_filter(
call_user_func_array('array_merge', $tmpResult),
function ($value) {
return $value !== NULL;
}
);
var_dump($result);
No matter how many elements in each array this will work.
var_export(
array_interlace($array1, $array2, $array3)
);
function array_interlace() {
$args = func_get_args();
$total = count($args);
if($total < 2) {
return FALSE;
}
$i = 0;
$j = 0;
$arr = array();
foreach($args as $arg) {
foreach($arg as $v) {
$arr[$j] = $v;
$j += $total;
}
$i++;
$j = $i;
}
ksort($arr);
return array_values($arr);
}
Results for updated question :
Array ( [0] => a [1] => e [2] => i [3] => b [4] => f [5] => j [6] => c [7] => g [8] => k [9] => d [10] => h [11] => l [12] => x [13] => s [14] => u [15] => d [16] => xx [17] => t )
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];
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.