3

Hello all im trying to find duplicate x values from this array and remove them and only leave the unique ones. For example my array is

Array
(
[0] => Array
    (
        [x] => 0.5
        [y] => 23
    )

[1] => Array
    (
        [x] => 23
        [y] => 21.75
    )

[2] => Array
    (
        [x] => 14.25
        [y] => 21.875
    )

[3] => Array
    (
        [x] => 19.375
        [y] => 21.75
    )

[4] => Array
    (
        [x] => 9.125
        [y] => 21.875
    )

[5] => Array
    (
        [x] => 23
        [y] => 19.625
    )

[6] => Array
    (
        [x] => 19.375
        [y] => 19.625
    ) 
)

So what i need to happen is loops through the entire thing and see the first x value as .5 then continue and whatever else has x as .5 remove it from the array so that at the end i have a array that looks like this

 Array
   (
[0] => Array
    (
        [x] => 0.5
        [y] => 23
    )

[1] => Array
    (
        [x] => 23
        [y] => 21.75
    )

[2] => Array
    (
        [x] => 14.25
        [y] => 21.875
    )

[3] => Array
    (
        [x] => 19.375
        [y] => 21.75
    )

[4] => Array
    (
        [x] => 9.125
        [y] => 21.875
    )
)

where all the X values are unique. I searched online and found this function to use but this doesnt seem to work:

 $result = array_map("unserialize", array_unique(array_map("serialize", $array)));    
Yeak
  • 2,470
  • 9
  • 45
  • 71
  • you can only use `array_unique` if the entire element has to be unique, not a value in a nested sub-array. – Barmar Feb 24 '14 at 23:11
  • 1
    Loop through the array, creating a new associative array that uses the `x` values as the key. For each element, test if `x` is already in the array; if not, add it, otherwise skip it. Then use `array_values` on the result to get the final array. – Barmar Feb 24 '14 at 23:13

5 Answers5

7

Just loop through and find unique values as you go:

$taken = array();

foreach($items as $key => $item) {
    if(!in_array($item['x'], $taken)) {
        $taken[] = $item['x'];
    } else {
        unset($items[$key]);
    }
}

Each the first time the x value is used, we save it - and subsequent usages are unset from the array.

jterry
  • 6,209
  • 2
  • 30
  • 36
0

array_unique compares string values, so you can create objects (with an overloaded __toString function) as an intermediate step.

class XKeyObj {
    public $x;
    public $y;

    public function XKeyObj($x, $y) {
        $this->x = $x;
        $this->y = $y;
    }

    public function __toString() { return strval($this->x); }
}

function array_to_xKey($arr) { return new XKeyObj($arr['x'], $arr['y']); }
function xKey_to_array($obj) { return array('x' => $obj->x, 'y' => $obj->y); }

$input = array(
    array('x' => 0.5, 'y' => 23),
    array('x' => 23, 'y' => 21.75),
    array('x' => 14.25, 'y' => 21.875),
    array('x' => 19.375, 'y' => 21.75),
    array('x' => 9.125, 'y' => 21.875),
    array('x' => 23, 'y' => 19.625),
    array('x' => 19.375, 'y' => 19.625)
);

$output = array_map('xKey_to_array',
                    array_unique(array_map('array_to_xKey', $input)));

print_r($output);

The result:

Array
(
    [0] => Array
        (
            [x] => 0.5
            [y] => 23
        )

    [1] => Array
        (
            [x] => 23
            [y] => 21.75
        )

    [2] => Array
        (
            [x] => 14.25
            [y] => 21.875
        )

    [3] => Array
        (
            [x] => 19.375
            [y] => 21.75
        )

    [4] => Array
        (
            [x] => 9.125
            [y] => 21.875
        )

)
Brian S
  • 4,878
  • 4
  • 27
  • 46
0

When performing iterated checks on arrays, the performance drag with in_array() will progressively worsen as your temporary lookup array increases in size.

With this in mind, use temporary associative keys to identify subsequent duplicates so that !isset() can be called on your growing result variable. Because php arrays are hash maps, this technique will consistently outperform in_array().

There IS a gotcha with these temporary keys which applies specifically to your float type values. When floats are used as array keys, php will convert them to integers by truncating ("flooring" not "rounding"). To avoid this unwanted side-effect, prepend a non-numeric character (other than a hyphen, of course) to the temporary keys so that the float becomes a string.

Code: (Demo)

$array = [
    ['x' => 0.5, 'y' => 23],
    ['x' => 23, 'y' => 21.75],
    ['x' => 14.25, 'y' => 21.875],
    ['x' => 19.375, 'y' => 21.75],
    ['x' => 9.125, 'y' => 21.875], 
    ['x' => 23, 'y' => 19.625],
    ['x' => 19.375, 'y' => 19.625],
];

foreach ($array as $row) {
    if (!isset($result['#' . $row['y']])) {
        $result['#' . $row['y']] = $row;
    }
}
var_export(array_values($result));

From PHP7.4+ enjoy the null coalescing assignment operator (Demo)

foreach ($array as $row) {
    $result['#' . $row['y']] ??= $row;
}
var_export(array_values($result));

Output:

array (
  0 => 
  array (
    'x' => 0.5,
    'y' => 23,
  ),
  1 => 
  array (
    'x' => 23,
    'y' => 21.75,
  ),
  2 => 
  array (
    'x' => 14.25,
    'y' => 21.875,
  ),
  3 => 
  array (
    'x' => 23,
    'y' => 19.625,
  ),
)

p.s. If dealing with string or integer values as temporay keys there is no need to prepend any characters. If you don't care about removing the temporary keys from the result (because you are only accessing the subarray values "down script", then you don't need to call array_values() after iterating.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

Late to the party: The obligatory one-liner.

$out = array_values ( array_intersect_key( $in, array_unique( array_column( $in, 'x' ) ) ) );

And step by step (with my own example):

$in = [
    [ 'k' => 'a' ],
    [ 'k' => 'a' ],
    [ 'k' => 'b' ]
];

print_r( array_column( $in, 'k' ) );

array_column returns a flat array, only with the values from all 'k'-keys:

Array
(
    [0] => a
    [1] => a
    [2] => b
)

We can now filter that with array_unique:

print_r( array_unique( array_column( $in, 'k' ) ) ) );

To get:

Array
(
    [0] => a
    [2] => b
)

See that we kept the keys of the original array (0 and 2)? We can use that with array_intersect_key. It computes the intersection of arrays using keys for comparison.

print_r( array_intersect_key( $in, array_unique( array_column( $in, 'k' ) ) ) );

We get the first [0] and third [2] entry of our original array.

Array
(
    [0] => Array
        (
            [k] => a
        )

    [2] => Array
        (
            [k] => b
        )

)

Now the keys are messed up (which might lead to problems if for loops), so we wrap the whole thing in array_values:

print_r(
  array_values( array_intersect_key( $in, array_unique( array_column( $in, 'k' ) ) ) ) 
);
Array
(
    [0] => Array
        (
            [k] => a
        )

    [1] => Array
        (
            [k] => b
        )

)

This question duplicates this one btw.

schuhwerk
  • 412
  • 5
  • 7
-2
array_unique(my_array, SORT_REGULAR)
SpencerX
  • 5,453
  • 1
  • 14
  • 21