I know about array_intersect_key which returns duplicate keys of the first parameter in any of the following parameters.
However I was wondering which would be the easiest way to find duplicate keys across more than two arrays at once? Does PHP offer such a function or do I need to do it with multiple calls?
Given
$a1 = array('hello'=>1, 'tag'=>1);
$a2 = array('moin'=>1);
$a3 = array('moin'=>1, 'tag'=>1);
Duplicate keys across all three arrays are moin and tag.
I thought so far about calling array_intersect_keys on each possible pair of parameters (in a function accepting a 2-n number of arrays as parameters) but have problems to actually find all possible combinations. And perhaps there is a much more easier way to do this.
Here's a custom function I made which does what you're looking for:
function array_duplicate_keys()
{
$keys = array();
foreach(func_get_args() as $arr)
{
if(!is_array($arr))
{
continue;
}
foreach($arr as $key => $v)
{
if(!isset($keys[$key]))
{
$keys[$key] = -1;
}
$keys[$key]++;
}
}
return array_keys(array_filter($keys));
}
$a1 = array('hello'=>1, 'tag'=>1);
$a2 = array('moin'=>1);
$a3 = array('moin'=>1, 'tag'=>1);
/*
print_r(array_duplicate_keys($a1, $a2, $a3));
Array
(
[0] => tag
[1] => moin
)
*/
I think your idea is good - call it on every possible combination of arrays. Two nested for loops should be enough to get all combinations:
function array_duplicate_keys() {
$arrays = func_get_args();
$count = count($arrays);
$dupes = array();
// Stick all your arrays in $arrays first, then:
for ($i = 0; $i < $count; $i++) {
for ($j = $i+1; $j < $count; $j++) {
$dupes += array_intersect_key($arrays[$i], $arrays[$j]);
}
}
return array_keys($dupes);
}
$a1 = array('hello'=>1, 'tag'=>1);
$a2 = array('moin'=>1);
$a3 = array('moin'=>1, 'tag'=>1);
$all = array($a1, $a2, $a3);
function pair_duplicate_keys($arrays) {
$keys = array();
$result = array();
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (!isset($keys[$key])) {
$keys[$key] = 1;
} else {
$result[$key] = 1;
}
}
}
return array_keys($result);
}
print_r(pair_duplicate_keys($all));
Output
Array
(
[0] => moin
[1] => tag
)
All you need is a simple call to array_merge function:
$a = array_merge($a1, $a2, $a3);
print_r($a);
OUTPUT
Array
(
[hello] => 1
[tag] => 1
[moin] => 1
)
Ugly, but does the job:
function array_duplicate_keys() {
return array_keys(array_filter(array_count_values(call_user_func_array('array_merge', array_map('array_keys', func_get_args()))), function ($num) {
return $num > 1;
}));
}
$a1 = array('hello'=>1, 'tag'=>1);
$a2 = array('moin'=>1);
$a3 = array('moin'=>1, 'tag'=>1);
print_r(
array_duplicate_keys($a1, $a2, $a3)
);
Output:
Array
(
[0] => tag
[1] => moin
)
Related
Let's say I Have two arrays.
$arr1 = ['A','B','C','D'];
$arr2 = ['C','D'];
now compare two arrays.if there is no match for value of $arr1 in $arr2 then index is left empty.
for the above arrays output should be:
$arr3 = ['','','C','D']
I tried array_search() function.But couldn't achieve desired output.
Any possible solutions?
You can use foreach with in_array and array_push like:
$arr1 = ['A','B','C','D'];
$arr2 = ['C','D'];
$arr3 = [];
foreach($arr1 as $value){
$arr3[] = (in_array($value, $arr2)) ? $value : '';
}
print_r($arr3);
/*
Result
Array
(
[0] =>
[1] =>
[2] => C
[3] => D
) */
Foreach the first array then test with in_array if exist, if true push into array 3 else push empty value
You can use the following function. In the following code, the function search_in_array return true and false based on the searching within the 2nd array. So you can push the empty or searched value in final array.
<?php
$arr1 = array('A','B','C','D');
$arr2 = array('C','D');
$arr3 = array();
function search_in_array ($value, $array)
{
for ($i=0; $i<count($array); $i++)
{
if ($value == $array[$i])
{
return true;
}
}
return false;
}
for ($i=0; $i<count($arr1); $i++)
{
$value = $arr1[$i];
$result = search_in_array ($value, $arr2);
if ($result)
{
array_push ($arr3, $value);
}
else
{
array_push ($arr3, '');
}
}
print_array($arr3);
?>
This question already has answers here:
Populate array of integers from a comma-separated string of numbers and hyphenated number ranges
(8 answers)
Closed 6 months ago.
I'm trying to normalize/expand/hydrate/translate a string of numbers as well as hyphen-separated numbers (as range expressions) so that it becomes an array of integer values.
Sample input:
$array = ["1","2","5-10","15-20"];
should become :
$array = [1,2,5,6,7,8,9,10,15,16,17,18,19,20];
The algorithm I'm working on is:
//get the array values with a range in it :
$rangeArray = preg_grep('[-]',$array);
This will contain ["5-10", "16-20"]; Then :
foreach($rangeArray as $index=>$value){
$rangeVal = explode('-',$value);
$convertedArray = range($rangeVal[0],$rangeVal[1]);
}
The converted array will now contain ["5","6","7","8","9","10"];
The problem I now face is that, how do I pop out the value "5-10" in the original array, and insert the values in the $convertedArray, so that I will have the value:
$array = ["1","2",**"5","6","7","8","9","10"**,"16-20"];
How do I insert one or more values in place of the range string? Or is there a cleaner way to convert an array of both numbers and range values to array of properly sequenced numbers?
Here you go.
I tried to minimize the code as much as i can.
Consider the initial array below,
$array = ["1","2","5-10","15-20"];
If you want to create a new array out of it instead $array, change below the first occurance of $array to any name you want,
$array = call_user_func_array('array_merge', array_map(function($value) {
if(1 == count($explode = explode('-', $value, 2))) {
return [(int)$value];
}
return range((int)$explode[0], (int)$explode[1]);
}, $array));
Now, the $array becomes,
$array = [1,2,5,6,7,8,9,10,15,16,17,18,19,20];
Notes:
Casts every transformed member to integer
If 15-20-25 is provided, takes 15-20 into consideration and ignores the rest
If 15a-20b is provided, treated as 15-20, this is result of casing to integer after exploded with -, 15a becomes 15
Casts the array keys to numeric ascending order starting from 0
New array is only sorted if given array is in ascending order of single members and range members combined
Try this:
<?php
$array = ["1","2","5-10","15-20"];
$newdata = array();
foreach($array as $data){
if(strpos($data,'-')){
$range = explode('-', $data);
for($i=$range[0];$i<=$range[1];$i++){
array_push($newdata, $i);
}
}
else{
array_push($newdata, (int)$data);
}
}
echo "<pre>";
print_r($array);
echo "</pre>";
echo "<pre>";
print_r($newdata);
echo "</pre>";
Result:
Array
(
[0] => 1
[1] => 2
[2] => 5-10
[3] => 15-20
)
Array
(
[0] => 1
[1] => 2
[2] => 5
[3] => 6
[4] => 7
[5] => 8
[6] => 9
[7] => 10
[8] => 15
[9] => 16
[10] => 17
[11] => 18
[12] => 19
[13] => 20
)
Problem solved!
Using range and array_merge to handle the non-numeric values:
$array = ["1","2","5-10","15-20"];
$newArray = [];
array_walk(
$array,
function($value) use (&$newArray) {
if (is_numeric($value)) {
$newArray[] = intval($value);
} else {
$newArray = array_merge(
$newArray,
call_user_func_array('range', explode('-', $value))
);
}
}
);
var_dump($newArray);
It's easier to find out the minimum and maximum value and create the array with them. Here's an example:
$in = ["1","2","5-10","15-20"];
$out = normalizeArray($in);
var_dump($out);
function normalizeArray($in)
{
if(is_array($in) && sizeof($in) != 0)
{
$min = null;
$max = null;
foreach($in as $k => $elem)
{
$vals = explode('-', $elem);
foreach($vals as $i => $val)
{
$val = intval($val);
if($min == null || $val < $min)
{
$min = $val;
}
if($max == null || $val > $max)
{
$max = $val;
}
}
}
$out = array();
for($i = $min; $i <= $max; $i++)
{
$out[] = $i;
}
return $out;
}
else
{
return array();
}
}
here you go mate.
<?php
$array = ["1","2","5-10","15-20"];
$newArr = array();
foreach($array as $item){
if(strpos($item, "-")){
$temp = explode("-", $item);
$first = (int) $temp[0];
$last = (int) $temp[1];
for($i = $first; $i<=$last; $i++){
array_push($newArr, $i);
}
}
else
array_push($newArr, $item);
}
print_r($newArr);
?>
Simpler and shorter answer.
See in Ideone
$new_array = array();
foreach($array as $number){
if(strpos($number,'-')){
$range = explode('-', $number);
$new_array = array_merge($new_array, range($range[0],$range[1]));
}
else{
$new_array[] = (int) $number;
}
}
var_dump($new_array);
try this:
$array = ["1","2","5-10","15-20"];
$result = [];
foreach ($array as $a) {
if (strpos($a,"-")!== false){
$tmp = explode("-",$a);
for ($i = $tmp[0]; $i<= $tmp[1]; $i++) $result[] = $i;
} else {
$result[] = $a;
}
}
var_dump($result);
you did not finish a little
$array = ["1","2","5-10","15-20"];
// need to reverse order else index will be incorrect after inserting
$rangeArray = array_reverse( preg_grep('[-]',$array), true);
$convertedArray = $array;
foreach($rangeArray as $index=>$value) {
$rangeVal = explode('-',$value);
array_splice($convertedArray, $index, 1, range($rangeVal[0],$rangeVal[1]));
}
print_r($convertedArray);
This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed 7 months ago.
I'd like to turn a simple multidimensional array into an even more simple array.
Turn this:
Array
(
[0] => Array
(
[id] => 123
)
[1] => Array
(
[id] => 456
)
...
[999] => Array
(
[id] => 789
)
)
Into an array like this:
Array
(
[0] => 123
[1] => 456
...
[999] => 789
)
I'd like to do so without foreach foreach. Is this possible in PHP?
Here's how I can already solve it with a foreach loop:
$newArr = array();
foreach ($arr as $a) {
$newArr[] = $a['id'];
}
$arr = $newArr;
I'd like to do it without looping. Can you help?
I admire your desire to have something approaching functional programming and implicit looping, but PHP is the wrong language for you. It does not express itself naturally in a functional style.
The reset function returns the first element in an array, so you can map that function over the array:
array_map('reset', $array)
However in PHP the fastest method is a simple for loop (not foreach, for). Here are a bunch of different methods of flattening. Only the functions containing for and foreach perform explicit looping and are included for comparison.
function flatten_for($arr) {
$c = count($arr);
$newarr = array();
for ($i = 0; $i < $c; $i++) {
$newarr[] = $arr[$i][0];
}
return $newarr;
}
function flatten_for_inplace($arr) {
$c = count($arr);
for ($i = 0; $i < $c; $i++) {
$arr[$i] = $arr[$i][0];
}
}
function flatten_foreach($arr) {
$newarr = array();
foreach ($arr as $value) {
$newarr[] = $value[0];
}
return $newarr;
}
function flatten_foreach_inplace($arr) {
foreach ($arr as $k => $v) {
$arr[$k] = $v[0];
}
}
function flatten_foreach_inplace_ref($arr) {
foreach ($arr as &$value) {
$value = $value[0];
}
}
function flatten_map($arr) {
return array_map('reset', $arr);
}
function flatten_walk($arr) {
array_walk($arr, function(&$v, $k){$v = $v[0];});
}
function visitor($v, $k, &$a) {
return $a[] = $v;
}
function flatten_walk_recursive($arr) {
$newarr = array();
array_walk_recursive($arr, 'visitor', $newarr);
return $newarr;
}
function reducer($result, $item) {
return $item[0];
}
function flatten_reduce($arr) {
return array_reduce($arr, 'reducer', array());
}
function flatten_merge($arr) {
return call_user_func_array('array_merge_recursive', $arr);
}
Here is the timing code:
function buildarray($length) {
return array_map(function($e){return array($e);}, range(0, $length));
}
function timeit($callable, $argfactory, $iterations) {
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
call_user_func($callable, call_user_func($argfactory));
}
return microtime(true) - $start;
}
function time_callbacks($callbacks, $argfactory, $iterations) {
$times = array();
foreach ($callbacks as $callback) {
$times[$callback] = timeit($callback, $argfactory, $iterations);
}
return $times;
}
function argfactory() {
return buildarray(1000);
}
$flatteners = array(
'flatten_for', 'flatten_for_inplace', 'flatten_foreach',
'flatten_foreach_inplace', 'flatten_foreach_inplace_ref',
'flatten_map', 'flatten_walk', 'flatten_walk_recursive',
'flatten_reduce', 'flatten_merge',
);
$results = time_callbacks($flatteners, 'argfactory', 1000);
var_export($results);
On an oldish MacBook Pro (Core 2 Duo, 2.66 GHz, 8GB, PHP 5.3.15 with Suhosin-Patch) I get these results:
array (
'flatten_for' => 12.793387174606,
'flatten_for_inplace' => 14.093497991562,
'flatten_foreach' => 16.71691608429,
'flatten_foreach_inplace' => 16.964510917664,
'flatten_foreach_inplace_ref' => 16.618073940277,
'flatten_map' => 24.578175067902,
'flatten_walk' => 22.884744882584,
'flatten_walk_recursive' => 31.647840976715,
'flatten_reduce' => 17.748590946198,
'flatten_merge' => 20.691106081009,
)
The difference between the for and foreach methods is smaller on longer arrays.
Surprisingly (to me, anyway) flatten_merge is still slower than a plain for loop. I expected array_merge_recursive to be at least as fast if not faster since it's basically handing the whole job off to a C function!
You could map it:
$arr = array_map(function($element) {
return $element['id'];
}, $arr);
Since array_map probably internally loops, you could do it truly without looping:
$arr = array_reduce($arr, function($arr, $element) {
$arr[] = $element['id'];
return $arr;
});
But there's no reason to not loop. There's no real performance gain, and the readability of your code is arguably decreased.
For now, the easiest way is to use the array_column() function
$newArray = array_column($array, 'id');
array one: 1,3,5,7
array two: 2,4,6,8
the array i want would be 1,2,3,4,5,6,7,8
I'm just using numbers as examples. If it was just numbers i could merge and sort but they will be words. So maybe something like
array one: bob,a,awesome
array two: is,really,dude
should read: bob is a really awesome dude
Not really sure how to do this. Does PHP have something like this built in?
You could write yourself a function like this:
function array_merge_alternating($array1, $array2) {
if(count($array1) != count($array2)) {
return false; // Arrays must be the same length
}
$mergedArray = array();
while(count($array1) > 0) {
$mergedArray[] = array_shift($array1);
$mergedArray[] = array_shift($array2);
}
return $mergedArray;
}
This function expects two arrays with equal length and merges their values.
If you don't need your values in alternating order you can use array_merge. array_merge will append the second array to the first and will not do what you ask.
Try this elegant solution
function array_alternate($array1, $array2)
{
$result = Array();
array_map(function($item1, $item2) use (&$result)
{
$result[] = $item1;
$result[] = $item2;
}, $array1, $array2);
return $result;
}
This solution works AND it doesn't matter if both arrays are different sizes/lengths:
function array_merge_alternating($array1, $array2)
{
$mergedArray = array();
while( count($array1) > 0 || count($array2) > 0 )
{
if ( count($array1) > 0 )
$mergedArray[] = array_shift($array1);
if ( count($array2) > 0 )
$mergedArray[] = array_shift($array2);
}
return $mergedArray;
}
Try this function:
function arrayMergeX()
{
$arrays = func_get_args();
$arrayCount = count($arrays);
if ( $arrayCount < 0 )
throw new ErrorException('No arguments passed!');
$resArr = array();
$maxLength = count($arrays[0]);
for ( $i=0; $i<$maxLength; $i+=($arrayCount-1) )
{
for ($j=0; $j<$arrayCount; $j++)
{
$resArr[] = $arrays[$j][$i];
}
}
return $resArr;
}
var_dump( arrayMergeX(array(1,3,5,7), array(2,4,6,8)) );
var_dump( arrayMergeX(array('You', 'very'), array('are', 'intelligent.')) );
var_dump( arrayMergeX() );
It works with variable numbers of arrays!
Live on codepad.org: http://codepad.org/c6ZuldEO
if arrays contains numeric values only, you can use merge and sort the array.
<?php
$a = array(1,3,5,7);
$b = array(2,4,6,8);
$merged_array = array_merge($a,$b);
sort($merged,SORT_ASC);
?>
else use this solution.
<?php
function my_merge($array1,$array2)
{
$newarray = array();
foreach($array1 as $key => $val)
{
$newarray[] = $val;
if(count($array2) > 0)
$newarray[] = array_shift($array2)
}
return $newarray;
}
?>
hope this help
Expects both arrays to have the same length:
$result = array();
foreach ($array1 as $i => $elem) {
array_push($result, $elem, $array2[$i]);
}
echo join(' ', $result);
Hey there I've made a recursive permutation function for class, but I output is less than favorable.
http://codepad.org/DOaMP9oc
function permute($arr) {
$out = array();
if (count($arr) > 1) {
$i = 0;
foreach($arr as $r => $c) {
$n = $arr;
unset($n[$r]);
$out[$c] = permute($n);
}
}
else
return array_shift($arr);
return $out;
}
If input is array(1,2,3,4,5), Output is:
Array
(
[1] => Array
(
[2] => Array
(
[3] => Array
(
[4] => 5
[5] => 4
)
[4] => Array
(
[3] => 5
[5] => 3
)
[5] => Array
(
[3] => 4
[4] => 3
)
)
ETC......................
This is all Correct, you can read it like this key.key.key.key.value or 12345,12354,12435
Currently, to convert this output to something readable, I'm using this ugly block of code:
http://codepad.org/qyWcRBCl
foreach($out as $k => $a)
foreach($a as $l => $b)
foreach ($b as $m => $c)
foreach ($c as $n => $d)
echo $k.$l.$m.$n.$d.'<br>';
How can I alter my function to eliminate the foreach stack and output in a similar format from permute().
My solution is to work on strings:
function permute($string) {
if (strlen($string)<2) {
return array($string);
}
$permutations = array();
// Copy everything but the first character of the string.
$copy = substr($string, 1);
foreach (permute($copy) as $permutation) {
$length = strlen($permutation);
// Insert the first character of the original string.
for ($i=0; $i<=$length; $i++) {
$permutations[] = substr($permutation, 0, $i) . $string[0] . substr($permutation, $i);
}
}
sort($permutations);
return $permutations;
}
header('Content-type:text/plain');
print_r(permute('12345'));
You already have a working implementation so I have no qualms in giving that to you. Note that the array is not created in order, so I simply sort it at the end. Also note that this only works on things that you intend to have the value of 1 character, so doing a permutation of car names would not work.
Even if you don't like this answer, I suggest that you use a type-hint for array:
function permute(array $arr) {
This will enforce that you pass an array into it.
function display_permutation($array){
if(is_array($array)){
foreach($array as $key => $val){
echo $key;
display_permutation($val);
}
}else{
echo $array;
}
}
Here is my permutation function and we can display results in simple way
class Permute {
public $results = Array();
public function __construct(array $array) {
$this->_permute($array);
}
private function _permute(array $orig, array $perm = Array()) {
if(!count($orig)) {
$this->results[] = $perm;
return null;
}
$count = count($orig);
for($i = 0; $i < $count; ++$i) {
$orig2 = $orig;
unset($orig2[$i]);
$orig2 = array_values($orig2);
$perm2 = $perm;
$perm2[] = $orig[$i];
$this->_permute($orig2, $perm2);
}
}
}
$arr = Array(1,2,3,4,5);
$permute = new Permute($arr);
foreach($permute->results as $result) {
echo join('', $result).'<br>';
}
I opted to use the following function:
function showPerms($a,$i='') {
if (is_array($a))
foreach($a as $k => $v)
showPerms($v,$i.$k);
else
echo $i.$a."\n";
}
However, I'd still like to do this in a singular recursive function.