PDA

View Full Version : arrange array by value of a key

Dubz
07-24-2012, 03:34 AM
I have an array that is set up with the following structure:

\$array = array(
0 => array(
'key1' => '1',
'key2' => '90rhfweoifew',
'status' => 1
),
1 => array(
'key1' => '7',
'key2' => '9w04jtgoprew',
'status' => 2
),
2 => array(
'key1' => '32423',
'key2' => 'jiewophg',
'status' => 0
),
);

Assume that the pattern continues, with key1 and key2 being random information that doesn't matter, and status is either 0, 1, or 2. My goal is to rearrange the \$array array so that if the status is 2, it is moved first, if it's 1, its moved second, and if its 0, i'ts moved last. Their will be multiple counts of 0's, 1's, and 2's, since its the only allowed value for the key. So basically, the array above will look like this:

\$array = array(
0 => array(
'key1' => '7',
'key2' => '9w04jtgoprew',
'status' => 2
),
1 => array(
'key1' => '1',
'key2' => '90rhfweoifew',
'status' => 1
),
2 => array(
'key1' => '32423',
'key2' => 'jiewophg',
'status' => 0
),
);

Fou-Lu
07-24-2012, 04:26 AM
Simple, use a callback sort.

function yourStatusSorterASC(\$a, \$b)
{
\$i = 0;
if (isset(\$a['status'], \$b['status']))
{
\$i = \$a['status'] - \$b['status'];
}
return \$i;
}

function yourStatusSorterDESC(\$a, \$b)
{
return yourStatusSorterASC(\$b, \$a);
}

usort(\$array, 'yourStatusSorterDESC');

print_r(\$array);

Dubz
07-24-2012, 06:10 AM
Simple, use a callback sort.

function yourStatusSorterASC(\$a, \$b)
{
\$i = 0;
if (isset(\$a['status'], \$b['status']))
{
\$i = \$a['status'] - \$b['status'];
}
return \$i;
}

function yourStatusSorterDESC(\$a, \$b)
{
return yourStatusSorterASC(\$b, \$a);
}

usort(\$array, 'yourStatusSorterDESC');

print_r(\$array);

Tried this but it didn't work. Also, another thing I need to add is a value check (forgot about it). What it needs to do is sort it this way (more complicated). Along with the status value is an ip value (key levels are the same, meaning status and ip are in the same array). I need to check if the ip matches the user's ip (only matters if status is 2; the ip is stored as \$ip on the page loading). The new order I need is something like this:

2 - Matching IP
1 - Any IP
2 - Non-matching IP
0- Any IP

I'm sure this is possible, but it could get messy in the long run. I never used callbacks before so I'll look into it, but if someone finds a solution, that would be great!

Fou-Lu
07-24-2012, 07:00 AM
Works for me.
Adding the ip is simply changing the weight. Since there is no condition for the 0 and matching IP, I assumed its always last. Won't sort order by the ip.

\$array = array(
array(
'key1' => '1',
'key2' => '90rhfweoifew',
'status' => 1,
'ip' => '12.45.67.89',
),
array(
'key1' => '7',
'key2' => '9w04jtgoprew',
'status' => 2,
'ip' => '123.45.67.89',
),
array(
'key1' => '32423',
'key2' => 'jiewophg',
'status' => 0,
'ip' => '123.45.67.89'
),
array(
'key1' => '7',
'key2' => '9w04jtgoprew',
'status' => 2,
'ip' => '12.45.67.89',
),
array(
'key1' => '7',
'key2' => '9w04jtgoprew',
'status' => 1,
'ip' => '123.45.67.89',
),
);

\$ip = '123.45.67.89';

function yourStatusSorterASC(\$a, \$b)
{
\$ip = \$GLOBALS['ip'];
\$i = 0;
if (isset(\$a['status'], \$b['status'], \$a['ip'], \$b['ip']))
{
\$i = \$a['status'] - \$b['status'];
if (\$a['status'] > 0)
{
\$i += strcmp(\$a['ip'], \$ip) == 0 ? 1 : 0;
}
if (\$b['status'] > 0)
{
\$i += strcmp(\$b['ip'], \$ip) == 0 ? -1 : 0;
}
}
return \$i;
}

function yourStatusSorterDESC(\$a, \$b)
{
return yourStatusSorterASC(\$b, \$a);
}

usort(\$array, 'yourStatusSorterDESC');

print_r(\$array);

Array
(
[0] => Array
(
[key1] => 7
[key2] => 9w04jtgoprew
[status] => 2
[ip] => 123.45.67.89
)

[1] => Array
(
[key1] => 7
[key2] => 9w04jtgoprew
[status] => 1
[ip] => 123.45.67.89
)

[2] => Array
(
[key1] => 7
[key2] => 9w04jtgoprew
[status] => 2
[ip] => 12.45.67.89
)

[3] => Array
(
[key1] => 1
[key2] => 90rhfweoifew
[status] => 1
[ip] => 12.45.67.89
)

[4] => Array
(
[key1] => 32423
[key2] => jiewophg
[status] => 0
[ip] => 123.45.67.89
)

)

Dubz
07-24-2012, 06:39 PM
Well so far I got it to do this:

2 - IP matches
1 OR 2 - Any IP
0 - Any IP

The second group isn't sorting as wanted, but its definitely better than before. I need to go in that group and put ones on top and twos under, so it looks like this:

2 - IP Matches
1 - Any IP
2 - IP doesn't match
0 - Any IP

It is trying to order it right, but it's ordering somewhat like this:

2 - IP Matches
1 - IP matches; 2 - Any IP <-- mixed together
1 - IP doesn't match
0 - Any IP

Fou-Lu
07-24-2012, 07:49 PM
Grouping it by a lower status and then sandwiching it between two identical status is going to be a task an a half to write an algorithm for.
I'd suggest coming up with a better set of value data in which to create a sort on.

Dubz
07-24-2012, 07:54 PM
Grouping it by a lower status and then sandwiching it between two identical status is going to be a task an a half to write an algorithm for.
I'd suggest coming up with a better set of value data in which to create a sort on.

The way it's set up is this:
0 - Unavailable
1 - Available
2 - Owed by someone

The IP only needs to affect the array if it's status is 2 (owned). The problem i have is getting it in an order I want it in. I'll play around with it later tonight then when i get the time.

Fou-Lu
07-24-2012, 08:09 PM
The way it's set up is this:
0 - Unavailable
1 - Available
2 - Owed by someone

The IP only needs to affect the array if it's status is 2 (owned). The problem i have is getting it in an order I want it in. I'll play around with it later tonight then when i get the time.

Remember that sorting algorithms compare one item to another item to determine where it lies in the list. With what you have to do now, you need to factor the ip provided by two entries, how these two are compared against a static entry, and how the status is compared against the other.
Its doable yes, but I don't expect that it will be as easy as it looks.

Dubz
07-24-2012, 08:20 PM
Remember that sorting algorithms compare one item to another item to determine where it lies in the list. With what you have to do now, you need to factor the ip provided by two entries, how these two are compared against a static entry, and how the status is compared against the other.
Its doable yes, but I don't expect that it will be as easy as it looks.

What if i have the list change the status to 3 if the ip matches? would the other sorter work then?

The status 2 needs to be between 1 and 0, not before them. it goes 2 -> 1 -> 0 right now, but (in this theory) it needs to go 3 -> 1 -> 2 -> 0

Fou-Lu
07-24-2012, 09:20 PM
Still won't work. You are attempting to sandwich the status of 2, 1, 1, 2, 0. Even if you change it to 3 for matching ip's, you'd have 3, 3, 1, 2, 0. That 1, 2, 0 isn't sortable without some really explicit comparisons, as in 0 is > 2, 2 > 1. So that puts you no further ahead since the same can already be applied at each status level anyways.

You just have to determine what the algorithm will be to compare these to each other. Matching IP + 2 > Matching IP + 1 > Any IP + 1 > Any IP + 2 > Any IP + 0. The results of the algorithm must do the comparison between two items within it as there is no other logical way to perform a sort other than comparison between items.

Arcticwarrio
07-24-2012, 09:54 PM
would be easier to use a database?

Dubz
07-24-2012, 10:13 PM
would be easier to use a database?

It is in a database...

The status modified to 3 on the page would ONLY be if the status was 2 AND the ip matched, which is what I would want first. The only thing that i need to sort out is to put the leftover statuses that are 2 AFTER statuses that are 1. The way it is now should be fine, because the ip is only there if the status is 2 (possibly 0). I'll have to play around with it later as I said and get it to work.

Fou-Lu
07-24-2012, 10:50 PM
Wait. Did you say that the ip only exists if the status is 2?
Post a relevant array. I assumed ip existed in all scenarios.

Dubz
07-24-2012, 10:56 PM
Wait. Did you say that the ip only exists if the status is 2?
Post a relevant array. I assumed ip existed in all scenarios.

It should only exist when it is 2, but it could be set if its not, I just wanted a fail safe in case. It doesn't matter if it's not 2 though, because the order is this:

2 - IP must match
1 - None/Any IP (doesn't matter if the ip is set or what it is)
2 - IP does not match
0 - None/Any IP (doesn't matter if the ip is set or what it is)

So if the status is 0, The IP won't matter, it will be last in the list. If it is 1, it won't matter and it will be second (or first if their are no first). If it is 2, the IP determines if it goes before the one, or after it (and before the 0).

Fou-Lu
07-24-2012, 11:01 PM
Ah then just as I thought before.

Arcticwarrio
07-24-2012, 11:09 PM
order the query by status before you build the array then

Dubz
07-25-2012, 12:00 AM
The last idea I had for it was to run a foreach 4 times and put the id's in a new array with the check first so it would be reordered, but it would take a ton of memory up since im talking about a list of 2k+ and being ran on every page load.

Added in the multiple foreach function and it works as wanted. It takes more memory, but it gets the job done.

function sortList(\$array)
{
global \$ip;
\$array2 = array();
foreach(\$array as \$i => \$arr)
{
if(\$arr['status'] == 2 && \$arr['ip'] == \$ip)
\$array2[] = \$arr;
}
foreach(\$array as \$i => \$arr)
{
if(\$arr['status'] == 1)
\$array2[] = \$arr;
}
foreach(\$array as \$i => \$arr)
{
if(\$arr['status'] == 2 && \$arr['ip'] != \$ip)
\$array2[] = \$arr;
}
foreach(\$array as \$i => \$arr)
{
if(\$arr['status'] == 0)
\$array2[] = \$arr;
}
return \$array2;
}