Home > Software design >  Filter array rows where two column values have been encountered before (in either order)
Filter array rows where two column values have been encountered before (in either order)

Time:11-29

I Have some problem with some value on array at php, here is the array

array:4 [ 
  0 => array:7 [
    "id" => 76
    "id_sender" => 1
    "id_receiver" => 2
    "message" => "2 Miliar"
    "is_read" => 0
    "created_at" => "2022-11-28T13:57:17.000000Z"
    "updated_at" => "2022-11-28T13:57:17.000000Z"
  ]
  1 => array:7 [
    "id" => 75
    "id_sender" => 1
    "id_receiver" => 3
    "message" => "1 Miliar"
    "is_read" => 0
    "created_at" => "2022-11-28T13:57:10.000000Z"
    "updated_at" => "2022-11-28T13:57:10.000000Z"
  ]
  2 => array:7 [
    "id" => 74
    "id_sender" => 3
    "id_receiver" => 1
    "message" => "Property ini berapa harganya?"
    "is_read" => 1
    "created_at" => "2022-11-28T13:52:57.000000Z"
    "updated_at" => "2022-11-28T13:55:37.000000Z"
  ]
  3 => array:7 [
    "id" => 73
    "id_sender" => 2
    "id_receiver" => 1
    "message" => "Untuk yang ini berapa harganya?"
    "is_read" => 1
    "created_at" => "2022-11-28T13:07:34.000000Z"
    "updated_at" => "2022-11-28T13:55:33.000000Z"
  ]
]

That's my array, I want to skip the the value on index 2 and 3 because I've already the value

for example

id_sender = 1 && id_receiver = 3,

because on index 2 i already have value, even it is reverse

id_sender = 3 && id_receiver = 1,

that's put on check of id_receiver of index 2, and another conditions and so one,

the result i want are like this

array:2 [ 
  0 => array:7 [
    "id" => 76
    "id_sender" => 1
    "id_receiver" => 2
    "message" => "2 Miliar"
    "is_read" => 0
    "created_at" => "2022-11-28T13:57:17.000000Z"
    "updated_at" => "2022-11-28T13:57:17.000000Z"
  ]
  1 => array:7 [
    "id" => 75
    "id_sender" => 1
    "id_receiver" => 3
    "message" => "1 Miliar"
    "is_read" => 0
    "created_at" => "2022-11-28T13:57:10.000000Z"
    "updated_at" => "2022-11-28T13:57:10.000000Z"
  ]
]

How can I solve that in php?

** EDIT **

I tried using array_filter, but it didn't change anything:

$member1 = 1;
$member2 = 3;

array_filter($items_message, function($v, $k) use ($member1, $member2) {
    return (($k == 'id_sender' && $v == $member1) && ($k == 'id_receiver' && $v == $member2 )) || (($k == 'id_sender' && $v == $member2) && ($k == 'id_receiver' && $v == $member1 ));
}, ARRAY_FILTER_USE_BOTH);

CodePudding user response:

First you need to find a way to know what you've seen so far while looping through your array.

A solid solution would be to use a pairing function, to create some unique number out of the 2 numbers you have. Pairing functions do not give the same result if the numbers are inverted, so we'll need to make sure we feed both numbers in the same order, to do that we'll use the smallest number of the two first. You'd then be able to easily determine if you already encountered the pair.

I am sorry my PHP is very rusty, so I'll use something similar but the code won't work.

function almostCantorPair(a, b){
  if(a > b){
    $big = $a;
    $small = $b;
  } else {
    $small = $a;
    $big = $b;
  }
  return (($small   $big) / 2) * ($small   $big   1)   $big
}

You would then loop through your array, take the return of this "almost" cantor pair (almost because we switch the numbers as explained above). You'd check if you have it in an array, if not you push the object to your result array and add that pair to your "known" array. If you find a pair, then you simply ignore the object.

$visitedPairs = new array();
$result = new Array();
foreach ($array as $elem) {

  $pair = almostCantorPair($elem['id_sender'], $elem['id_receiver']);
  if(!in_array($pair,$visitedPairs)){
    array_push($result,$elem);
    array_push($visitedPairs,$pair);
  } 
}

About pairing function, Wikipedia sums it up very well:

In mathematics, a pairing function is a process to uniquely encode two natural numbers into a single natural number.

CodePudding user response:

While iterating, isolate the sender and receiver ids as a temporary array, sort the two elements, then implode them as a string -- this will give you a reliable value to use as the key in result array. With these new associative first-level keys in your result array, you can use the null coalescing assignment operator to only store the first occurring row for each respective id set.

Code: (Demo)

var_export(
    array_values(
        array_reduce(
            $array,
            function($result, $row) {
                $ids = [$row['id_sender'], $row['id_receiver']];
                sort($ids);
                $result[implode(',', $ids)] ??= $row;
                return $result;
            }
        )
    )
);

If you prefer a classic loop to iterate, then this is equivalent to the above snippet (Demo)

$result = [];
foreach ($array as $row) {
    $ids = [$row['id_sender'], $row['id_receiver']];
    sort($ids);
    $result[implode(',', $ids)] ??= $row;
}
var_export(array_values($result));

Relevant reading: Filter/Remove rows where column value is found more than once in a multidimensional array

  • Related