10

This may be some sort of weird longer shortcut, and please correct me if I'm mistaken in this train of thought...

I have a matrix of data that looks like:

unique_id | URL | other random data...
unique_id | URL | other random data...
unique_id | URL | other random data...

I want to be able to reference an item by either its URL, or its unique_id. Is there a fancy way to do this?

I suppose the cheating solution would be to just make two arrays, but is there is a better way?

3
  • 2
    Not really, no. Just make two arrays. The overhead is minimal (well, depending on how big your arrays are). Commented Dec 10, 2010 at 3:12
  • Do you need to modify the "random data" once it's in the array? And it it possible for a unique_id and a URL to collide? Commented Dec 10, 2010 at 3:12
  • Don't duplicate "other random data" ("DRY" concept). The cleanest solution is to have one array that maps unique_id to other random data, then a second array that maps url to unique_id. That is the purpose of unique_ids: to provide a concise and efficient "handle" that represents a given record (row) of data. Commented Dec 23, 2020 at 20:24

4 Answers 4

14

Only way I can think of that doesn't involve iterating the array for each search (see Jacob's answer) is to store references to each item in two arrays.

Edit: As the URLs and IDs cannot collide, they may be stored in the same reference array (thanks Matthew)

$items; // array of item objects
        // Use objects so they're implicitly passed by ref

$itemRef = array();

foreach ($items as $item) {
    $itemRef[$item->unique_id] = $item;
    $itemRef[$item->url] = $item;
}

// find by id
$byId = $itemRef[$id];

// find by url
$byUrl = $itemRef[$url];

You could probably encapsulate this nicely using a collection class that implements getById() and getByUrl(). Internally, it could store the references in as many arrays as is necessary.

Of course, what you're essentially doing here is creating indexed result sets, something best left to database management systems.

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

4 Comments

Also, you can put them in the same array (e.g. $items) if you choose. It might be slightly confusing, but Bob said the keys can't collide.
Oh that is interesting. They don't collide, but it seems a little scary from a vulnerability stand point (for example a lookup that was forced using a url instead of a unique_id). Nice technique though
@Bob It's similar to when you retrieve database records using a fetch_both method where the result contains both numeric and string indexes. Speaking of indexes, if this data does come from a DB, you're probably better off applying indexes to the ID and url fields and doing single fetches to retrieve nominated records.
@MatthewFlaschen - you could put them in the same array, but its cleaner to have a second array that maps url to unique_id. Keep separate concepts separate.
3

Try something like this:

function selectByIdOrURL($array, $data) {
    foreach($array as $row) {
       if($row['unique_id'] == $data || $row['url'] == $data) return $row;
    }
    return NULL;
}

$array = array(
           array('unique_id' => 5, 'url' => 'http://blah.com'),
           array('unique_id' => 3, 'url' => 'http://somewhere_else.com')
         );
$found = selectByIdOrURL($array, 5); //array('unique_id' => 5, 'url' => 'http://blah.com')
$nfound = selectByIdOrURL($array, 10); //NULL

5 Comments

Is the only way to iterate the data? I suppose it's not a huge deal, but I'm really curious if you can have multiple indexes for arrays (generally speaking)
@Bob, no, there is no way to have multiple indexes for arrays.
Generally speaking, no. The fastest approach (for access times) is to have two arrays, one with the unique_id as the key, the other with the URL, and both with all the data as the value. Jacob's answer will use slightly less memory, but will need to iterate the whole array each time you reference something; my approach will use slightly more memory, but will retrieve the results faster.
That's a shame... I guess just iterating it isn't so bad
Any fancy solution would involve iteration behind the scenes anyway.
1

It appears your fancy solution was only available as of PHP 5.5. You can combine the use of array_search and array_column to fetch your entry in a single line of code:

$items = [
    [
     'unique_id' => 42,
     'url' => 'http://foo.com'
    ],
    [
     'unique_id' => 57,
     'url' => 'http://bar.com'
    ],
    [
     'unique_id' => 36,
     'url' => 'http://example.com'
    ],

];

$bar = $entries[array_search(57, array_column($items, 'unique_id'))];

var_dump($bar);

//outputs
array (size=2)
    'unique_id' => int 57
    'url' => string 'http://bar.com' (length=14)

Comments

0

Surely an object would be the easy way?

class Item {
    public $unique_url;
    public $url;
    public $other_data;

    public function __construct($unique_url, $url, $other_data)
    {
        $this->unique_url = $unique_url;
        $this->url = $url;
        $this->other_data = $other_data;
    }
}



class ItemArray {
    private $items = array();

    public function __construct()
    {
    }

    public function push(Item $item)
    {
        array_push($items, $item); //These may need to be reversed
    }

    public function getByURL($url)
    {
        foreach($items as $item)
        {
            if($item->url = $url)
            {
                return $item;
            }
        }
    }

    public function getByUniqueURL($url)
    {
        foreach($items as $item)
        {
            if($item->unique_url = $unique_url)
            {
                return $item;
            }
        }
    }

}

Then use it with

$itemArray = new ItemArray();
$item = new Item("someURL", "someUniqueURL","some other crap");
$itemArray->push($item);

$retrievedItem = $itemArray->getItemByURL("someURL");

This technique has a little extra overhead due to object creation, but unless you're doing insane numbers of rows it would be fine.

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.