views:

162

answers:

5

I have two arrays in php that are part of an image management system.

weighted_images A multidimensional array. Each sub array is an associative array with keys of 'weight' (for ordering by) and 'id' (the id of the image).

array(
    156 => array('weight'=>1, 'id'=>156),
    784 => array('weight'=>-2, 'id'=>784),
)

images This array is user input. It's an array of image ids.

array(784, 346, 748)

I want to combine them in to a single array of ids ordered by the weight of the image. If an image doesn't have a weight append to the end.

It's not a particularly hard problem however my solution is far from elegant and can't help thinking that there must be a better way to do this.

$t_images = array();
foreach ($weighted_images as $wi) {
  if ( in_array($wi['id'], $images) ) {
    $t_images[$wi['weight']] = $wi['id'];
  }
}
foreach ($images as $image) {
  if ( !$weighted_images[$image] ) {
    $t_images[] = $image;
  }
}
$images = $t_images;

Question: Is there a better way to do this?

A: 
<?php
$weights = array(
    156 => array('weight'=>1, 'id'=>156),
    784 => array('weight'=>-2, 'id'=>784),
);

$selected = array(784, 346, 748);

$selectedWeights = array();
foreach ($selected as $id)
{
    $weight = 0;
    if (isset($weights[$id]))
    {
        $weight = $weights[$id]['weight'];
    }
    $selectedWeights[$id] = $weight;
}
asort($selectedWeights);

print_r($selectedWeights);
?>
Sjoerd
A: 

If I've understood you well:

$data = array(
156 => array('weight'=>1, 'id'=>156),
784 => array('weight'=>-2, 'id'=>784),
);
$ids = array(156, 784, 431);


function compare_weight($item1, $item2) {
    return $item1['weight'] > $item2['weight'] ? 1 : -1;
}

uashort($data, 'compare_weight');

foreach($ids as $id)
    $data += array($id => array('weight'=>null, 'id'=>$id) );
Skirmantas
The final array needs to hold only the images in the `images` array but also contain the weights of those images if they are contained in `weighted_images`.
Annan
A: 

You can get the intersection of the arrays rather easily:

$selected_images = array_intersect_key($weighted_images, array_fill_keys($images, null))

The array_fill_keys function uses the $images array for the keys and null for the value of each key. Since we are intersecting the arrays using the keys (array_intersect_key), it doesn't matter what the values are for any of the arrays except for the first.

Then you can sort by weight using a callback function as Skirmantas suggested:

function cmp_weight($a, $b)
{
    if ($a['weight'] == $b['weight']) {
        return 0;
    }

    return (($a['weight'] > $b['weight']) ? 1 : -1;
}

$images = uasort($selected_images, 'cmp_weight');

If you are using PHP 5.3 you can use an anonymous function:

$images = uasort($selected_images, function($a, $b)
{
    if ($a['weight'] == $b['weight']) {
        return 0;
    }

    return (($a['weight'] > $b['weight']) ? 1 : -1;
})
Schmalls
+1  A: 

Schmalls is almost right, just missed the last step -

If an image doesn't have a weight append to the end.

Here is the full process.

$array = array_intersect_key($weighted_images, array_fill_keys($images, null));

uasort($array, function($a, $b) {
    if($a['weight'] == $b['weight']) return 0;
    return ($a['weight'] > $b['weight']) ? 1 : -1;
});

$array += array_diff_key($images, $weighted_images);
Chase
A: 

I would start rethinking the $weighted_images array. Something like this, where key is ID and value is WEIGHT, could be enough:

$weighted_images = array(
  156 => 1,
  784 => -2,
);
$images = array(156, 784, 431);

Then just do some ordering, and a foreach to be sure you have all the images in the array.

// Images (with weight) ordered
asort($weighted_images);

// Check all images and add at the end the ones with no weight, with null value
foreach ($images as $id) {
  if (!array_key_exists($id, $weighted_images)) {
    $weighted_images[$id] = null;
  }
}

That's it.

illarra
This assumes that -2 weight is 'higher' than 1. If not, you should use arsort() instead of asort().
illarra