Remove common items from multiple arrays - php

$array1 = array("a","b");
$array2 = array("b","c");
I want the following array from above arrays containing items which are not common in both arrays.
$output = array("a","c");
I have tried folloing
$output = array_diff($array1,$array2);
How can do it. Thanks.

Just make subtraction of union and intersection:
$array1 = array("a","b");
$array2 = array("b","c");
$output = array_diff(array_merge($array1, $array2), array_intersect($array1, $array2));

Use:
$array1 = array("a","b");
$array2 = array("b","c");
$output = array("a","c");
$output = array_merge(array_diff($array1,$array2),array_diff($array2,$array1));
print_r($output);

So I read earlier today about how array_merge was considered slow. I also read the same about array_diff, specifically here. This got me to thinking that using both at once could be particularly expensive, so I put the following experiment together:
I expanded upon merlyn's function to come up with this:
function multiArrayUnique($array1, $array2)
{
//leaves array1 intact
$arrayFrom1=$array1;
$arrayAgainst = array_flip($array2);
foreach ($arrayFrom1 as $key => $value) {
if(isset($arrayAgainst[$value])) {
unset($arrayFrom1[$key]);
}
}
//arrayFrom1=array("a");
$arrayFrom2=$array2;//b,c
$arrayAgainst = array_flip($array1);//a,b
foreach ($arrayFrom2 as $key => $value) {
if(isset($arrayAgainst[$value])) {
unset($arrayFrom2[$key]);
}
}
//arrayFrom2=array("c");
foreach ($arrayFrom2 as $item) {
array_push($arrayFrom1, $item);
}
return $arrayFrom1;
}
Using the OP's example, this function also returns array("a","c").
Now, that is a monstrous amount of code, and it it difficult to read... but, here is the efficiency it buys on a large data set:
$a1 = range(0,25000);
$a2 = range(15000,50000);
$start=microtime(true);
$output = array_diff(array_merge($a1, $a2), array_intersect($a1, $a2));
$end=microtime(true);
echo $end-$start."<hr>";
7.5844340324402
$start=microtime(true);
$output = array_merge(array_diff($a1,$a2),array_diff($a2,$a1));
$end=microtime(true);
echo $end-$start."<hr>";
6.551374912262
$start=microtime(true);
$result=multiArrayUnique($a1,$a2);
$end=microtime(true);
echo $end-$start."<hr>";
0.21001195907593
So while the function's code is considerably more complex, and may have other limitations, it's implementation is still a one-liner, and the processing time is 14x faster.

Related

PHP: Delete unique values from an array

I would like to delete all unique values from an array.
So lets say I have $array = (1,2,3,5,4,3,4,5,234)
the function should delete all unique values and output:
$newarray = (3,5,4,3,4,5)
I just thought of a solution with array_count_values but I do not know how I could iterate it. Furthermore I am sure there is a more elegant and efficient way to do this. Thank you for your help in advance.
One of solutions:
$array = [1,2,3,5,4,3,4,5,234];
$freq = array_count_values($array);
print_r(array_filter(
$array,
function ($v) use ($freq) { return 1 < $freq[$v]; }
));
$array = (1,2,3,5,4,3,4,5,234);
$freq = array_count_values($array);
$output = array();
foreach($array as $val){
if($freq[$val] >1){
$output[] = $val;
}
}
print_r($output);
May be not the most efficient way.

Replace array value with more than one values

I have an array like this,
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
I want to find any value with an ">" and replace it with a range().
The result I want is,
array(
1,2,3,4,5,6,7,8,9,10,11,12, '13.1', '13.2', 14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
);
My understanding:
if any element of $array has '>' in it,
$separate = explode(">", $that_element);
$range_array = range($separate[0], $separate[1]); //makes an array of 4 to 12.
Now somehow replace '4>12' of with $range_array and get a result like above example.
May be I can find which element has '>' in it using foreach() and rebuild $array again using array_push() and multi level foreach. Looking for a more elegant solution.
You can even do it in a one-liner like this:
$array = array(1,2,3,'4>12','13.1','13.2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,#range(...array_slice(explode(">","$c>$c"),0,2)));},
[]
));
I avoid any if clause by using range() on the array_slice() array I get from exploding "$c>$c" (this will always at least give me a two-element array).
You can find a little demo here: https://rextester.com/DXPTD44420
Edit:
OK, if the array can also contain non-numeric values the strategy needs to be modified: Now I will check for the existence of the separator sign > and will then either merge some cells created by a range() call or simply put the non-numeric element into an array and merge that with the original array:
$array = array(1,2,3,'4>12','13.1','64+2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,strpos($c,'>')>0?range(...explode(">",$c)):[$c]);},
[]
));
See the updated demo here: https://rextester.com/BWBYF59990
It's easy to create an empty array and fill it while loop a source
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$res = [];
foreach($array as $x) {
$separate = explode(">", $x);
if(count($separate) !== 2) {
// No char '<' in the string or more than 1
$res[] = $x;
}
else {
$res = array_merge($res, range($separate[0], $separate[1]));
}
}
print_r($res);
range function will help you with this:
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$newArray = [];
foreach ($array as $item) {
if (strpos($item, '>') !== false) {
$newArray = array_merge($newArray, range(...explode('>', $item)));
} else {
$newArray[] = $item;
}
}
print_r($newArray);

Nested loops and array formation

Suppose that I start with an array that looks like:
$array_1 = array(array(1,2,3), array(2,4,5), array(3,6,7));
For simplicity, assume that I have a rule that says: delete the first subarray and then delete the first elements of the remaining subarrays. This would yield the result:
$new_array = array(array(4,5), array(6,7))
Then assume I expand the problem to larger arrays like:
$array_2 = array(array(1,2,3,4), array(2,3,4,5), array(3,4,5,6), array(4,5,6,7));
I have the same rule here - delete first subarray and then delete first elements of the remaining subarrays. BUT this rule must be continued until the smallest subarray contains only two elements (as in the first example). So that in stage one of the process, my new array would look like:
$new_array_s1 = array(array(3,4,5), array(4,5,6), array(5,6,7));
But in the final stage, the completed array would look like:
$new_array_s2 = array(array(5,6), array(6,7));
For context, here is my code for the $array_1 example:
<?php
$array_1 = array(array(1,2,3), array(2,4,5), array(3,6,7));
$array_shell = $array_1;
unset($array_shell[0]);
$array_size = count($array_shell);
$i = 0;
$cofactor = array();
while($i < $array_size) {
$el_part_[$i] = $array_1[$i];
unset($el_part_[$i][0]);
$el_part_[$i] = array_values($el_part_[$i]);
array_push($cofactor, $el_part_[$i]);
++$i;
}
echo '<pre>',print_r($cofactor,1),'</pre>';
?>
My Question: How can I generalise this code to work for N sized arrays?
You don't need a complicated code .. Just loop and use array_shift
Example:
print_r(cleanUp($array_1));
Function
function cleanUp($array) {
array_shift($array);
foreach($array as $k => $var) {
is_array($var) && array_shift($array[$k]);
}
return $array;
}
See Live DEMO
$num = count($array_1);
for($i=0;$i<=$num;$i++)
{
if($i==0)
unset($array_1[$i]);
else unset($array_1[$i][0]);
}
Building off of Baba's answer, to work with N element arrays (assuming each array contains the same number of elements):
<?php
$array_1 = array(array(1,2,3,4), array(2,4,5,6), array(3,6,7,8));
$array = $array_1;
while(count($array[0]) > 2)
$array = cleanUp($array);
print_r($array);
function cleanUp($array) {
array_shift($array);
foreach($array as $k => $var) {
is_array($var) && array_shift($array[$k]);
}
return $array;
}
This will keep reducing until the sub-arrays have only 2 elements.
-Ken

php explode and force array keys to start from 1 and not 0

I have a string that will be exploded to get an array, and as we know, the output array key will start from 0 as the key to the first element, 1 for the 2nd and so on.
Now how to force that array to start from 1 and not 0?
It's very simple for a typed array as we can write it like this:
array('1'=>'value', 'another value', 'and another one');
BUT for an array that is created on the fly using explode, how to do it?
Thanks.
$exploded = explode('.', 'a.string.to.explode');
$exploded = array_combine(range(1, count($exploded)), $exploded);
var_dump($exploded);
Done!
Just use a separator to create a dummy element in the head of the array and get rid of it afterwards. It should be the most efficient way to do the job:
function explode_from_1($separator, $string) {
$x = explode($separator, $separator.$string);
unset($x[0]);
return $x;
}
a more generic approach:
function explode_from_x($separator, $string, $offset=1) {
$x = explode($separator, str_repeat($separator, $offset).$string);
return array_slice($x,$offset,null,true);
}
$somearray = explode(",",$somestring);
foreach($somearray as $key=>$value)
{
$otherarray[$key+1] = $value;
}
well its dirty but isn't that what php is for...
Nate almost had it, but needed a temporary variable:
$someArray = explode(",",$myString);
$tempArray = array();
foreach($someArray as $key=>$value) {
$tempArray[$key+1] = $value;
}
$someArray = $tempArray;
codepad example
$array = array('a', 'b', 'c', 'd');
$flip = array_flip($array);
foreach($flip as &$element) {
$element++;
}
$normal = array_flip($flip);
print_r($normal);
Try this, a rather funky solution :P
EDIT: Use this instead.
$array = array('a', 'b', 'b', 'd');
$new_array = array();
$keys = array_keys($array);
for($i=0; $i<count($array); $i++) {
$new_array[$i+1] = $array[$i];
}
print_r($new_array);
I agree with #ghoti that this task is probably an XY Problem. I can't imagine a valid/professional reason to start keys from 1 -- I've never needed this functionality in over 10 years of development. I'll offer a compact looping approach, but I'll probably never need it myself.
After instatiating a counter which is one less than the desired first key, you can use a body-less foreach() as a one-liner.
Code: (Demo)
$i = 0;
$result = [];
foreach (explode('.', 'a.string.to.explode') as $result[++$i]);
var_export($result);

PHP merge array(s) and delete double values

WP outputs an array:
$therapie = get_post_meta($post->ID, 'Therapieen', false);
print_r($therapie);
//the output of print_r
Array ( [0] => Massagetherapie )
Array ( [0] => Hot stone )
Array ( [0] => Massagetherapie )
How would I merge these arrays to one and delete all the exact double names?
Resulting in something like this:
theArray
(
[0] => Massagetherapie
[1] => Hot stone
)
[SOLVED] the problem was if you do this in a while loop it wont work here my solution, ty for all reply's and good code.: Its run the loop and pushes every outcome in a array.
<?php query_posts('post_type=therapeut');
$therapeAr = array(); ?>
<?php while (have_posts()) : the_post(); ?>
<?php $therapie = get_post_meta($post->ID, 'Therapieen', true);
if (strpos($therapie,',') !== false) { //check for , if so make array
$arr = explode(',', $therapie);
array_push($therapeAr, $arr);
} else {
array_push($therapeAr, $therapie);
} ?>
<?php endwhile; ?>
<?php
function array_values_recursive($ary) { //2 to 1 dim array
$lst = array();
foreach( array_keys($ary) as $k ) {
$v = $ary[$k];
if (is_scalar($v)) {
$lst[] = $v;
} elseif (is_array($v)) {
$lst = array_merge($lst,array_values_recursive($v));
}}
return $lst;
}
function trim_value(&$value) //trims whitespace begin&end array
{
$value = trim($value);
}
$therapeAr = array_values_recursive($therapeAr);
array_walk($therapeAr, 'trim_value');
$therapeAr = array_unique($therapeAr);
foreach($therapeAr as $thera) {
echo '<li><input type="checkbox" value="'.$thera.'">'.$thera.'</input></li>';
} ?>
The following should do the trick.
$flattened = array_unique(call_user_func_array('array_merge', $therapie));
or the more efficient alternative (thanks to erisco's comment):
$flattened = array_keys(array_flip(
call_user_func_array('array_merge', $therapie)
));
If $therapie's keys are strings you can drop array_unique.
Alternatively, if you want to avoid call_user_func_array, you can look into the various different ways of flattening a multi-dimensional array. Here are a few (one, two) good questions already on SO detailing several different methods on doing so.
I should also note that this will only work if $therapie is only ever a 2 dimensional array unless you don't want to flatten it completely. If $therapie is more than 2 dimensions and you want to flatten it into 1 dimension, take a look at the questions I linked above.
Relevant doc entries:
array_flip
array_keys
array_merge
array_unique
call_user_func_array
It sounds like the keys of the arrays you are generating are insignificant. If that's the case, you can do a simple merge then determine the unique ones with built in PHP functions:
$array = array_merge($array1, $array2, $array3);
$unique = array_unique($array);
edit: an example:
// Emulate the result of your get_post_meta() call.
$therapie = array(
array('Massagetherapie'),
array('Hot stone'),
array('Massagetherapie'),
);
$array = array();
foreach($therapie as $thera) {
$array = array_merge($array, $thera);
}
$unique = array_unique($array);
print_r($unique);
PHP's array_unique() will remove duplicate values from an array.
$tester = array();
foreach($therapie as $thera) {
array_push($tester, $thera);
}
$result = array_unique($tester);
print_r($result);

Categories