1

I have an array with the following structure:

[
    ["venue1" => 1, "venue2" => 2],
    ["venue1" => 3, "venue2" => 4],
    ["venue1" => 2, "venue2" => 1],
    ["venue1" => 5, "venue2" => 6],
]

I need to remove the duplicate "pair of values", in this case row [0] and row [2].

I tried it with that code, but it doesn't work (and of course it's not very elegant).

foreach ( $compare_arr as $v1 ) 
{
    $key = array_search( intval($v1[venue1]), array_column( $compare_arr, 'venue2' ) );
    if ( $key <> '' ) unset($compare_arr[$key]);
}
3

3 Answers 3

1

Here is an approach where an intermediate array is formed of sorted values. That you can then search for to find duplicate pairs to remove.

<?php

$venues = array(
    array(
      'venue1' => 1,
      'venue2' => 2,
    ),
    array(
      'venue1' => 3,
      'venue2' => 4,
    ),
    array (
      'venue1' => 2,
      'venue2' => 1,
    ),
    array(
      'venue1' => 5,
      'venue2' => 6,
    ),
);

$result = $pairs = $venues;
array_walk($pairs, static fn(array &$value) => sort($value));

var_export($pairs);

foreach ($pairs as $k => $pair) 
{
    if (count(array_keys($pairs, $pair)) > 1) 
    {
        unset($result[$k]);
    }
}

var_export($result); 

Output:

array (
    0 => 
    array (
      0 => 1,
      1 => 2,
    ),
    1 => 
    array (
      0 => 3,
      1 => 4,
    ),
    2 => 
    array (
      0 => 1,
      1 => 2,
    ),
    3 => 
    array (
      0 => 5,
      1 => 6,
    ),
  )array (
    1 => 
    array (
      'venue1' => 3,
      'venue2' => 4,
    ),
    3 => 
    array (
      'venue1' => 5,
      'venue2' => 6,
    ),
  )

Demo

If you want to remove occurring duplicates rather than pruning out duplicates altogether, you can do an array_unique on the sorted array above and then use the remaining keys to filter the original array.

$tmp     = $venues;
array_walk($tmp, static fn (array &$value) => sort($value));
$tmp     = array_unique($tmp, SORT_REGULAR);
$result  = array_intersect_key($venues, $tmp);
var_export($result);

Output:

array (
    0 => 
    array (
      'venue1' => 1,
      'venue2' => 2,
    ),
    1 => 
    array (
      'venue1' => 3,
      'venue2' => 4,
    ),
    3 => 
    array (
      'venue1' => 5,
      'venue2' => 6,
    ),
  )

Demo

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot! That looks very good! :-) It it possible not to completely remove both duplicate pairs and instead let one of them stay? (so that the result is an array with 1/2, 3/4 and 5/6 )
@o.heinrich you can just use array_unique to obtain unique occurrences on the intermediate array above, and use that to filter the original array. Added an example.
0

You might also first loop the array creating a compound key based on the ordered keys.

Then you can filter the result only keeping arrays where the count is 2 as nothing is added because there are no duplicates.

For example

$result = [];
$compare_arr = [
    ["venue1" => 1, "venue2" => 2],
    ["venue1" => 3, "venue2" => 4],
    ["venue1" => 2, "venue2" => 1],
    ["venue1" => 5, "venue2" => 6],
];

foreach ($compare_arr as $v1) {
    sort($v1);
    $cKey = $v1[0] .'-'. $v1[1];
    if (array_key_exists($cKey, $result)) {
        $result[$cKey][] = $v1;
        continue;
    }
    $result[$cKey] = $v1;
}
$result = array_filter($result, function($item) {
    return count($item) === 2;
});

print_r($result);

Output

Array
(
    [3-4] => Array
        (
            [0] => 3
            [1] => 4
        )

    [5-6] => Array
        (
            [0] => 5
            [1] => 6
        )

)

You can see the compound keys are the values with a - in between. If you want to have the keys numbered from 0, you can use array_values.

Php demo

Edit

If you want to keep the first matching single pair, you can check for the compound key and if it already exists continue the loop without overwriting the existing one.

$result = [];
$compare_arr = [
    ["venue1" => 1, "venue2" => 2],
    ["venue1" => 3, "venue2" => 4],
    ["venue1" => 2, "venue2" => 1],
    ["venue1" => 5, "venue2" => 6]
];

foreach ($compare_arr as $v1) {
    sort($v1);
    $cKey = $v1[0] .'-'. $v1[1];
    if (array_key_exists($cKey, $result)) {
        continue;
    }
    $result[$cKey] = $v1;
}

print_r($result);

Output

Array
(
    [1-2] => Array
        (
            [0] => 1
            [1] => 2
        )

    [3-4] => Array
        (
            [0] => 3
            [1] => 4
        )

    [5-6] => Array
        (
            [0] => 5
            [1] => 6
        )

)

Php demo

2 Comments

This a a great solution as well! :-) How can it be modified so that one of the duplicate pairs remains? (as a result an array with 1/2, 3/4 and 5/6 is needed)
@o.heinrich I have added another example for that scenario.
0

Whether you use a classic foreach() loop or functional iteration, there is no reason to iterate the input array more than once.

This snippet will appear nearly identical to TheFourthBird's answer, but I don't like the unnecessary use of continue. This snippet will ensure no that rows in the result array have 100% shared venue values (in any order). The subarray keys will also not suffer reordering; in other words the first element key will be venue1 then the second element will be venue2. Using implode() offers additional flexibility because the code won't need to be altered if the number of elements in each row changes.

$result = [];
foreach ($data as $row) {
    sort($row);
    $key = implode('-', $row);
    $result[$key] ??= $row;
}
var_export(array_values($result));

Output:

array (
  0 => 
  array (
    'venue1' => 1,
    'venue2' => 2,
  ),
  1 => 
  array (
    'venue1' => 3,
    'venue2' => 4,
  ),
  2 => 
  array (
    'venue1' => 5,
    'venue2' => 6,
  ),
)

To completely remove all rows where venue values are shared, maintain a "found" array as well as a "result" array.

Code: (Demo)

$result = [];
foreach ($data as $index => $row) {
    sort($row);
    $key = implode('-', $row);
    if (!isset($found[$key])) {
        $found[$key] = true;
        $result[$key] = $data[$index];
    } else {
        unset($result[$key]);
    }
}
var_export(array_values($result));

Or mutate the input array directly. Demo

foreach ($data as $index => $row) {
    sort($row);
    $key = implode('-', $row);
    if (!isset($found[$key])) {
        $found[$key] = $index;
    } else {
        unset($data[$index], $data[$found[$key]]);
    }
}
var_export(array_values($data));

Output:

array (
  0 => 
  array (
    'venue1' => 3,
    'venue2' => 4,
  ),
  1 => 
  array (
    'venue1' => 5,
    'venue2' => 6,
  ),
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.