Find pairings in a two-dimensional array - php

I have a two-dimensional array and look for a way to find the all double entries. E.g. if the array is of the form
$a = array(
array('a','b','c'),
array('d','a','e'),
array('d','c','b')
)
the function should return the list
array(array(0,0),array(1,1)) // Since $a[0,0]=$a[1,1]
array(array(0,1),array(2,2)) // Since $a[0,1]=$a[2,2]
array(array(0,2),array(2,1)) // Since $a[0,2]=$a[2,1]
array(array(1,0),array(2,0)) // Since $a[1,0]=$a[2,0]
array(1,2) // Unmatched
Is there an elegant/efficient way to implement this?

With a nested loop. Use the values as keys in the result and append the coordinates as arrays under those keys.
foreach ($a as $x => $inner) {
foreach ($inner as $y => $value) {
$result[$value][] = [$x, $y];
}
}
The $result will contain sets of coordinates for all the given values. You can group the results by size of set to identify which values are unmatched, pairs, or have even greater than two occurrences (if that's possible).
foreach ($result as $value => $set) {
$sets[count($set)][$value] = $set;
}

Related

PHP - Elegantly extract the numeric indices in array a that are not in array b (not array_diff_key)

Suppose you have two arrays $a=array('apple','banana','canaple'); and $b=array('apple');, how do you (elegantly) extract the numeric indices of elements in array a that aren't in array b? (in this case, indices: 1 and 2).
In this case, array a will always have more elements than b.
Note, this is not asking for array_diff_key, but rather the numeric indices in the array with more elements that don't exist in the array with fewer elements.
array_diff gets you half way there. Using array_keys on the diff gets you the rest of what you want.
$a = ['apple','banana','canaple'];
$b = ['apple'];
$diff = array_diff($a, $b);
$keys = array_keys($diff);
var_dump($keys); // [1, 2]
This is because array_diff returns both the element and it's key from the first array. If you wanted to write a PHP implementation of array_diff it might look something like this...
function array_diff(Array ... $arrays) {
$return = [];
$cmp = array_shift($arrays);
foreach ($cmp as $key => $value) {
foreach($arrays as $array) {
if (!in_array($value, $array)) {
$return[$key] = $value;
}
}
}
return $return;
}
This gives you an idea how you might achieve the result, but internally php implements this as a sort, because it's much faster than the aforementioned implementation.

How to remove values from an array if occurring more than one time?

I need to remove values from an array that occur more than one time in the array.
For example:
$value = array(10,10,5,8);
I need this result:
$value = array(5,8);
Is there any in-built function in php?
I tried this, but this will not return my expected result:
$unique = array_unique($value);
$dupes = array_diff_key($value, $unique);
You can use array functions and ditch the foreach loops if you wish:
Here is a one-liner:
Code:
$value = [10, 10, 5, 8];
var_export(array_keys(array_intersect(array_count_values($value),[1])));
As multi-line:
var_export(
array_keys(
array_intersect(
array_count_values($value),
[1]
)
)
);
Output:
array (
0 => 5,
1 => 8,
)
This gets the value counts as an array, then uses array_intersect() to only retain values that occur once, then turns the keys into the values of a zero-index array.
The above snippet works identically to #modsfabio's and #axiac's answers. The ONLY advantage in my snippet is brevity. It is possible that their solutions may outperform mine, but judging speed on relatively small data sets may be a waste of dev time. For anyone processing relatively large data sets, do your own benchmarking to find the technique that works best.
For lowest computational/time complexity, use a single loop and as you iterate conditionally populate a lookup array and unset() as needed.
Code: (Demo) (Crosslink to my CodeReview answer)
$values = [10, 10, 5, 8];
$found = [];
foreach ($values as $index => $value) {
if (!isset($found[$value])) {
$found[$value] = $index;
} else {
unset($values[$index], $values[$found[$value]]);
}
}
var_export($values);
// [2 => 5, 3 => 8]
A couple of notes:
If processing float values, using a technique that stores the values as keys (as all of my snippets do), then the results may be incorrect because php will change floats to integers when used as keys.
PHP is consistently much faster at searching for keys than it is at searching for values.
You can do it like this using array_count_values() and a foreach loop:
<?php
$input = array(10,10,5,8);
$output = array();
foreach(array_count_values($input) as $value => $count)
{
if($count == 1)
{
$output[] = $value;
}
}
var_dump($output);
Output:
array(2) {
[0]=>
int(5)
[1]=>
int(8)
}
Example: https://eval.in/819461
A possible approach:
$value = array(10,10,5,8);
$output = array_keys(
array_filter(
array_count_values($value),
function ($count) {
return $count == 1;
}
)
)
array_count_values() produces an array that associates to each unique value from $value the number of times it appears in the array.
array_filter() keeps in this result only the entries (the keys) that appear only once in the original array.
array_keys() produces the desired result.
I would use array_count_values to get an array with how often every element occurs in the array. Then remove all the elements from the original array that occur more than once.
You need to use array_count_values(), array_search() and unset() functions.
<?php
$value = array(10,10,5,8);
echo '<pre>';print_r($value);echo '</pre>';
$cnt = array_count_values($value);
$dup = array();
foreach ($cnt as $k => $repeated) {
if ($repeated > 1) {
if(($key = array_search($k, $value)) !== false) {
unset($value[$key]);
}
}
}
echo '<pre>';print_r($cnt);echo '</pre>';
echo '<pre>';print_r($value);echo '</pre>';
?>
Demo
you can use
foreach loop
and
array_diff() function:
$value=array(10,10,5,8);
$duplicated=array();
foreach($value as $k=>$v)
{
if($kt=array_search($v,$value))!==false and
$k!=$kt)
{if (count(array_keys($array, $value)) > 1)
{
/* Execute code */
}
unset($value[$kt];$duplicated[]=$v;
}
}
$result=array_diff($values,$duplicated);
print_r($result);
output
Array([2]=>5[3]=>8)

Push something into a specified position in an array in php

if I give you an array:
$a = array('something' => 'value', 'apple' => 'sauce');
and said given the key, find the position and insert something before it - what would you do?
I can find something by doing:
function insert($value, $array)
foreach($array as $k=>$v) {
if ($k === $value) {
// I am stuck here ...
}
}
}
But I don't know how to insert before $k in the array. This example assumes the array always has key=>value.
Update 1
Apologies all, I don't think I was very clear. Given the above information the goal is to use this "insert" function to insert a new key=>value, so given the above sample array, I want to do:
insert(array('new_key' => 'new_value'), 'something', $a)
So the above for loop would have to be changed to:
function insert($array, $key, $originalArray)
foreach($originalArray as $k=>$v) {
if ($k === $key) {
// Insert $array right before 'something'
// I am stuck here ...
}
}
}
The result would be a new array that looks like:
$a = array('new_key' => 'new_value', 'something' => 'value', 'apple' => 'sauce');
The goal is to find the key in the given array and insert the new array right before it. Both arrays must be key/value.
Here is a function which will insert an element into an array before or after the chosen value. It returns the modified array.
function associativeArrayInject($originalArray, $targetKey, $newKey, $newValue, $insertBefore=true){
# We will build a new array from the ground up, and return it. This is that array.
$newArray = array();
# Loop over the original array.
foreach($originalArray as $key => $value){
# If we need to inject the data before the current key, we
# do that here
if($key === $targetKey && $insertBefore)
$newArray[$newKey] = $newValue;
# At this point, we insert the $originalArray's key and value
# because if we needed to inject before, it's already been done,
# and if we need to inject after, we'll do that next
$newArray[$key] = $value;
# If we need to inject the data after the current key, we
# do that here
if($key === $targetKey && !$insertBefore)
$newArray[$newKey] = $newValue;
}
# When all the array values are looped over, the new array will have
# been constructed with the new data in the appropriate spot. Now
# we can return it.
return $newArray;
}
Sol. 1: Try to use arsort() or similar functions of sorting arrays in php.
Sol. 2: At first init array of values and than use foreach loop to sort array how you like.
Sol. 3: You can create multidimensional array, something like this:
$array[$position]['something']['value'];
And than use ksort function to sort array by keys. So, this is my idea.
Also, show exactly how you wana sort array. ( like input ( some data ) => output ( some data ) )
Several variations whether you want to modify in place with a reference or return the new array:
function insert(&$array, $search, $value) {
$i = 0;
foreach($array as $k => $v) {
if ($k === $search) {
return array_splice($array, $i+1, 0, $value);
}
$i++;
}
}
$a = array('foo' => 'bar', 'something' => 'value', 'apple' => 'sauce');
insert($a, 'something', 'new value');

Grab ordinal number of the array value inside foreach

Hello,
I want to grab the ordinal number of an array key inside a foreach loop.
Example
<?php
$array = array("id" => 2, "username" => "foobar");
foreach($array as $value){
$array_key = search_array($value, $array);
//find the ordinal number of $array_key from $array here.
echo $value;
}
count returns the entire number of array keys in the array, i need to grab the ordinal number of the array key.
I hope you guys understand what i ask.
From what I understand, if an entry has a string key, it doesn't have an ordinal position in the array. From http://php.net/manual/en/language.types.array.php:
An array in PHP is actually an ordered map. A map is a type that associates values to keys. This type is optimized for several different uses; it can be treated as an array, list (vector), hash table (an implementation of a map), dictionary, collection, stack, queue, and probably more. As array values can be other arrays, trees and multidimensional arrays are also possible.
Ordered maps don't assign ordinal keys on top of the already existing string keys.
What you could do though, to get a psuedo-ordinal-key is increment a variable.
$i=0;
foreach( $array as $key => $value ) {
echo $i.':'.$key.':'.$value;
$i++;
}
Will echo out each ordinal key, key, and value in the array.
Have another variable that will increase in value after each iteration.
You can use the current function like so:
foreach($array as $value){
$array_key = search_array($value, $array);
echo current( $array );
echo $value;
}
Or you can just add a counter to your loop like so:
$count = 0;
foreach($array as $value){
$array_key = search_array($value, $array);
echo $count++;
echo $value;
}
<?php
$arry = array("id" => 2; "username" => "foobar");
$idx = 0;
foreach($array as $value){
if(array_search(arry, $value)) echo "element found: ".$idx;
$idx++;
}
?>
What you need is another variable - $idx in the above example to count the iterations, as you can't use the "key" corresponding to the value as they're named.
Also the search function is called array_search indeed.

Interleaving multiple arrays into a single array

I need to merge several arrays into a single array. The best way to describe what I'm looking for is "interleaving" the arrays into a single array.
For example take item one from array #1 and append to the final array. Get item one from array #2 and append to the final array. Get item two from array #1 and append...etc.
The final array would look something like this:
array#1.element#1
array#2.element#1
.
.
.
The "kicker" is that the individual arrays can be of various lengths.
Is there a better data structure to use?
for example,
function array_zip_merge() {
$output = array();
// The loop incrementer takes each array out of the loop as it gets emptied by array_shift().
for ($args = func_get_args(); count($args); $args = array_filter($args)) {
// &$arg allows array_shift() to change the original.
foreach ($args as &$arg) {
$output[] = array_shift($arg);
}
}
return $output;
}
// test
$a = range(1, 10);
$b = range('a', 'f');
$c = range('A', 'B');
echo implode('', array_zip_merge($a, $b, $c)); // prints 1aA2bB3c4d5e6f78910
If the arrays only have numeric keys, here's a simple solution:
$longest = max( count($arr1), count($arr2) );
$final = array();
for ( $i = 0; $i < $longest; $i++ )
{
if ( isset( $arr1[$i] ) )
$final[] = $arr1[$i];
if ( isset( $arr2[$i] ) )
$final[] = $arr2[$i];
}
If you have named keys you can use the array_keys function for each array and loop over the array of keys instead.
If you want more than two arrays (or variable number of arrays) then you might be able to use a nested loop (though I think you'd need to have $arr[0] and $arr[1] as the individual arrays).
I would just use array_merge(), but that obviously depends on what exactly you do.
This would append those arrays to each other, while elements would only be replaced when they have the same non-numerical key. And that might not be a problem for you, or it might be possible to be solved because of attribute order, since the contents of the first arrays' elements will be overwritten by the later ones.
If you have n arrays, you could use a SortedList, and use arrayIndex * n + arrayNumber as a sort index.

Categories