Keeping even numbered elements of an array? - php

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.

Related

how to bind the value of a variable to another variable in php

I have 3 arrays:
$q1 = ['A', 'B', 'C', 'D'];
$q2 = ['E', 'F', 'G', 'H'];
$q3 = ['I', J', 'K', 'L'];
When i click on submit in a form, i store a session and everytime i click on next, the session will increment with 1
session_start();
if(!isset($_SESSION['i'])) {
$_SESSION['i'] = 0;
}
if(isset($_POST['next'])){
$_SESSION['i']++;
}
$session = $_SESSION['i'];
echo $session;
Now i want to bind the value of the session to the variable $q
So after 1 submit, $q must become $q1, after second submit; $q must become $q2 and so on...
So everytime i submit, the value of the session must be bind to $q, so that i can read different arrays.
(I want to use this for creating a dynamic form:)
foreach ($q as $key => $value) {
...
How can i do that?
Instead of variables - use array:
// I use explicit indexing, as you start with `i = 1`
$q = [
1 => ['A', 'B', 'C', 'D'],
2 => ['E', 'F', 'G', 'H'],
3 => ['I', 'J', 'K', 'L'],
];
$_SESSION['i'] = 3;
print_r($q[$_SESSSION['i']]);
Your code already looks fine and almost done.
So I use another array, which stores all your other arrays.
If you now get your $session variable, you can access the wrapper array and get your specific array you want. As you started your array names with 1, but arrays getting called with starting index 1, you have to subtract - 1.
$q1 = array('A', 'B', 'C', 'D');
$q2 = array('E', 'F', 'G', 'H');
$q3 = array('I', 'J', 'K', 'L');
$wrapper = array($q1, $q2, $q3);
$session = 2;
foreach ($wrapper[$session-1] as $key) {
//Will output E, F , G H as session is =2
echo $key;
}
Recommended
You can use PHP array to do this.
$arr = [
1 => ['A', 'B', 'C', 'D'],
2 => ['E', 'F', 'G', 'H'],
3 => ['I', J', 'K', 'L'],
];
then, access it like:
print_r($arr[$_SESSION['i']]);
Not ready to use Arrays?Well, PHP also allows you to use dynamic variable names, called Variable variables.
$variableName = "q";
//..... Update the value of i in session.
$variableName .= $_SESSION['i'];// when i = 1, it becomes q1;
print_r($$variableName); // notice the $$ in the Variable Name
// output
// Array (0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D')
Read more about Variable Variables here
https://www.php.net/manual/en/language.variables.variable.php

Chunk and transpose a flat array into rows with a specific number of columns

An array of values are provided in the order they should appear in columns. For example, the second value in the array should be displayed on a webpage beneath the first instead of on its right.
Task: Reorder the array so that the values are in the order they will be output in html. The data for the first row must be first in the output.
Example inputs:
$input = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
$cols = 2;
Example output:
[['A', 'E'], ['B','F'], ...]
Use array_chunk() to break the array up in to $cols:
$chunks = array_chunk($array, ceil(count($array) / $cols));
Which would give you:
array(array('A', 'B', 'C', 'D'), array('E', 'F', 'G'));
Then combine the values from each array where the keys match.
array_unshift($chunks, null);
$chunks = call_user_func_array('array_map', $chunks);
// array(array('A', 'E'), array('B', 'F'), array('C', 'G'), array('D', NULL))
Here's an example
You need to count offset and then iterate over an array:
$input = array('A', 'B', 'C', 'D', 'E', 'F', 'G');
$cols = 2;
$itemsCount = count($input);
$offset = ceil($itemsCount/$cols);
$result = array();
for($i = 0; $i < $itemsCount; ++$i) {
if (isset($input[$i]) && isset($input[$i + $offset])) {
$result[] = array($input[$i], $input[$i + $offset]);
}
}
The modern functional equivalent of #billyonecan's approach uses the spread operator to transpose the data after it has been "chunked" to the correct size. (Demo)
$input = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
$cols = 2;
var_export(
array_map(
null,
...array_chunk($input, ceil(count($input) / 2))
)
);
// [['A', 'E'], ['B', 'F'], ['C', 'G'], ['D', null]]
A classic loop alternative using a modulus calculation is also attractive because it doesn't generate the extra null element in the final set when the division doesn't come out even: (Demo)
$input = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
$cols = 2;
$rows = ceil(count($input) / $cols);
$result = [];
foreach ($input as $i => $value) {
$result[$i % $rows][] = $value;
}
var_export($result);
// [['A', 'E'], ['B', 'F'], ['C', 'G'], ['D']]
On this related topic, to more simply chunk and transpose with a finite number of rows, use the following: (Demo)
$rows = 2;
var_export(
array_map(
null,
...array_chunk($input, $rows)
)
);

php how to cut array and add other charactor to it

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;

Transpose and flatten two-dimensional indexed array where rows may not be of equal length

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));

Finding patterns in php array

For example:
main array is: array(0 => 'A', 1 => 'A', 2 => 'B', 3 => 'B', 4 => 'B');
pattern is: array('A', 'B');
expected answer: array( array(0, 2), array(1, 3) )
one more example:
main array array(0 => 'F', 5 => 'G', 78 => 'R', 2 => 'D');
pattern array('G', 'R', 'F');
expected answer: array(array(5, 78, 0))
How can I find all occurrences of pattern in array?
Here's an function that uses recursion.
function array_pattern($array, $pattern){
$ret = array(array());
$found = true;
foreach($pattern as $val){
$x = array_search($val, $array);
if($x === FALSE){
$found = FALSE;
break;
}
unset($array[$x]);
$ret[0][] = $x;
}
return $found ? array_merge($ret, array_pattern($array, $pattern)) : array();
}
Call it like this:
$a = array_pattern(array(0 => 'A', 1 => 'A', 2 => 'B', 3 => 'B', 4 => 'B'), array('A', 'B'));
$b = array_pattern(array(0 => 'F', 5 => 'G', 78 => 'R', 2 => 'D'), array('G', 'R', 'F'));
DEMO: http://codepad.org/JCdsAMGk
I didn't test the following code, but it may give you ideas.
$arr = array(0 => 'A', 1 => 'A', 2 => 'B', 3 => 'B', 4 => 'B');
$test = array('G', 'R', 'F');
$count = 0;
$count2 = 0;
for($i=0;$i<count($arr);$++){
$pass= true;
if(count($test)+$count <= count($arr)){
for($k=0;$k<count($test);$k++){
if($arr[k+i]!=$test[k]){
$pass = false;
}
}
}else{
$pass = false;
}
if($pass){
$output[$count2] = $i;
$count2++;
}
}

Categories