Jump to content

[SOLVED] Sorting a multi-dimentional array


gin

Recommended Posts

This has been giving me SUCH a headache :(

 

I have an array like so (of course, there are many more items than shown):

<?php
Array (
[0] => Array (
	[award] => Finalist - Public Service Messages and Cause Appeal Category
	[year] => 2006
	[name] => Big Huge Award
	[project] => 20
	[section] => animation
	[brand] => asdf
	[title] => Funky title
)
)
?>

 

I want to sort it by [award] in this order: Gold, Silver, Bronze, Finalist. I'm getting nowhere with uksort. The problem is the callback function. So far I've got:

<?php
function cmp($a, $b) {
$sortorder = array('gold', 'silver', 'bronze', 'finalist');
if ($a['position'] == $b['position'])
	return strcmp($a['name'], $b['name']);
$cmpa = array_search($a['position'], $sortorder);
$cmpb = array_search($b['position'], $sortorder);
return ($cmpa > $cmpb) ? 1 : -1;
}
?>

 

I need to modify this so that the keywords 'gold', 'silver', etc. are picked out of the long [award] field. Could someone point me on my way?

Link to comment
Share on other sites

this is about how you set your array up. In terms of searching an array with the availble php functions and constructs its actually better to group your sub arrays by their type....

 

SO you would have...

 

Array

(

'award' => array('Finalist - Public Service Messages and Cause Appeal Category', 'Finalist - Private Death Camp Service in Guantanamo', .....),

'year' => array(2006,2007,....)

 

.... and so on.

 

);

 

this would allow you to search the types you want or apply array_multisort to the array and do what ever...

 

now I could put som emore code down - but then you would lose teh pleasure of learning it yourself and seeing how much easier it is to work with an array constructed as I have shown..)

 

Link to comment
Share on other sites

Thanks very much for your reply!

Your code piece makes no sense to me, but if I'm reading the explanation correctly... well this is my attempt:

 

<?php
Array (
    [some Award] => Array (
	[4] => Array (
		[0] => Array (
			[award] => Finalist - PSA
			[year] => 2006
			[name] => Some Award
			[project] => 20
		)
	)
)
    [some Other Award] => Array (
	[4] => Array (
		[0] => Array (
			[award] => Finalist - PSA
			[year] => 2006
			[name] => Some Other Award
			[project] => 26
		)
		[1] => Array (
			[award] => Finalist - yadda yadda
			[year] => 2007
			[name] => Some Other Award
			[project] => 26
		)
	)
	[1] => Array (
		[0] => Array (
			[award] => Gold - Animation
			[year] => 2006
			[name] => Some Other Award
			[project] => 4
		)
	)
)
)
?>

 

Now however, to my utter annoyance, the Finalists are listed before the Gold. Is there any way to use array_multisort to sort according to keys, or should I just give in and foreach-ksort my way through?

Link to comment
Share on other sites

I disagree with that (ToonMariner) and believe it's all down to preference.

Personlly i like the format that has been chosen by gin. It allows extra fields to be added to certain items and not to others. Using your array setup (which is just as valid) i would have to make sure that i created a new array and populated it with mostly blank information except for the particular item i wanted to added the extra field to. Not ideal.

Also array_multisort only has a view available options, regex, numeric, or string, of which gin's pattern fits none.

This might just be my technical inexperience of usage of array_multisort but that's the impression i get from the php docs.

 

What you need to define is your own sorting algorithm.

Link to comment
Share on other sites

The array is actually generated from a database. All items have the same fields, so that's not an issue. The second post is my attempt at a brute force method of sorting (if it can be called that). It's just the same information placed in the order I'll eventually want to display it in.

 

The display, incidentally, will look something like:

 

Some Award

- Gold - Bestest in the world

  Title: asdf asdf (2006)

- Finalist - PSA

  Title: qwer qwer (2006)

 

Some Other Award

- Gold - Bestest in the world

  Title: asdf asdf (2006)

- Silver - PSA

  Title: qwer qwer (2006)

 

Anyway, I'm kinda resigned to the foreach-ksort method at the moment. I was just hoping someone knew of a sort I'd never heard of that would do all I want without the gazillion loops.

Link to comment
Share on other sites

@aschk - by all means disagree but the proof is in the pudding! The array I porposed is MUCH easier to work with...

 

@gin you have managed to create a 3D array which will be even harder to work with!!!

 

you have got hung up on keeping all the data of one record grouped together... think of your database table - its a series of records that all have a field - my array is just that it stores teh the data for each field in one array and the association is by the key - just as the database i stored in fileds and the association is made by the record number...

 

you shoudl end up with a 2-D array like so...

 

<?php

$array = array
(
'award' => array	(
		'Finalist - Public Service Messages and Cause Appeal Category',
		'Finalist - Private Death Camp Service in Guantanamo',
		.....
		),
'year' => array	(
		2006,
		2007,
		....
		)
);
.... and so on.

?>

 

the you could do somethink like

 

$findyear = array_keys($array['year'], 2007);

 

the result would be all the keys of $array['year'] that were 2007 and use them to get the information out of the other arrays (like $array['award']) using those keys.  Because you have this associative key you have much more flexibility in what you can achieve...

Link to comment
Share on other sites

<?php
function cmp($a, $b)
{
    $pos = array (
        'gold'     => 1,
        'silver'   => 2,
        'bronze'   => 3,
        'finalist' => 4
    );
    list ($a1, $c1) = explode('-', $a['award']);
    list ($a2, $c2) = explode('-', $b['award']);
    $catcmp = strcmp(trim($c1), trim($c2));
    if ($catcmp==0)
        return $pos[trim($a1)] - $pos[trim($a2)];
    else return $catcmp;
}

usort ($myarray, 'cmp');

usort ($myarray, 'cmp');
?>

Link to comment
Share on other sites

Okey dokey, having taken your example toon i've written it out a little further with some sample data.

This is what i'm after...

 

All the awards for the year 2007, ORDERED as gold, then silver, then bronze, then finalists...

 

<?php

$array = array
(
'award' => array	(
		'Finalist - Public Service Messages and Cause Appeal Category',
		'Finalist - Private Death Camp Service in Guantanamo',
		'Finalist - 3rd post',
		'Silver - ertae aerg aer g',
		'Gold - lkerjgklaejr  arar',
		'Bronze - el;araek lrjlkrja',
		),
'year' => array	(
		2006,
		2007,
		2007,
		2007,
		2007,
		2007,
		)
);

$findyear = array_keys($array['year'], 2007);
for($i = 0; $i<count($findyear); $i++){
echo $array["award"][$i]."<br/>";
}

?>

 

What we have in the above a list of awards... fantastic, but how do you order then??? I think you've missed the point of the question.

 

I'm going to propose that your database structure could use some modification gin.

You need a separate field for "position" with it being "gold", "silver", "bronze", or "finalist" linked by another table, with an "order" column also. Thus you can have

Award_Types
####################
id | type    | order
====================
1  | gold    | 1
2 | bronze   | 3
3 | finalist | 4
4 | silver   | 2

 

Now should you ever have another position (platinum) you just add it in at the bottom and rejig your ordering, AND voila your SQL statement will STILL continue to order by the order in the table.

Link to comment
Share on other sites

The main problem that structure is it tries to squeeze 2 columns into one.

 

Finalist - Public Service Messages and Cause Appeal Category

 

should be this, at least

[pre]

| award    |  category                                |

------------|--------------------------------------------+

| Finalist  |  Public Service Messages and Cause Appeal |

[/pre]

 

and better still if category were a foreign key linked to a category table.

 

Then you do a sort in the original guery

 

SELECT * FROM awardtable
ORDER BY category, FIELD(award, 'gold', 'silver', 'bronze', 'finalist')

 

 

 

 

Link to comment
Share on other sites

Nice variation on my solution Barand :)

 

I should just explain the reasoning behind my solution better :

 

Your SQL statement doesn't change if you add another category, where as in Barand's example you need to alter your SQL statement. Mine would be ORDER BY award_category.`order` no matter how many categories you have. Also, Barand's option provides no referential integrity for your data, meaning you could have "Finalist" , "finalist", "finlist", or some other horrid variation your input.

Link to comment
Share on other sites

 

What we have in the above a list of awards... fantastic, but how do you order then??? I think you've missed the point of the question.

 

 

array_multisort()

 

IF you take barands advice on the 'award' field but store numeric values 1-4 with 1 representing gold, 2 silver and so on this will be a piece of excreation....

Link to comment
Share on other sites

Can you elaborate on my test example then using array_multisort so that I may see a working solution.

 

Also, yes you're correct you could interchange numeric values with the names, however where do you change the numbers for their string representations? PHP Constants? They lose their meaning if you just use numbers, hence the extra table requirement. Of course you could just interpret those numeric in PHP, but with what? Constants? Is it maintable, nope ;)

 

Of course at the end of the day, each person's choice is there own and i'm sure gin will find an applicable solution be it one of ours or not. All are valid, and i'm certainly not trying to preach or control any particular channel of thinking on the matter. Just trying to offer another solution, with drawbacks/pitfalls and all.

 

I will continue to be a PHP pest until I can't see the faults in others and my own designs ;)

Link to comment
Share on other sites

Thank you everyone for all your suggestions. Right now, for the sake of expediency, I'm using nested loops, but I'm certainly interested in learning how to work smarter.

 

@ToonMariner: Your solution is definitely close to what I'm looking for, assuming I can wrap my brain around the array_multisort(). I need to go tinker with my code and try out Barand's code.

 

@Barand & aschk: I did in fact consider separating the award and category into different fields. Unfortunately, the awards aren't so cooperative as to have plain old "gold, silver, bronze" awards. It varies from Gold to Gold World Medal to Winner to.... you get the idea. Frankly, I should have just added another field, "sort_order", or similar. This is what I get for not planning things out properly.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.