lemaster777 Posted July 20, 2016 Share Posted July 20, 2016 I am trying to read data from an XML file that I do not control, which contains FileID's. I need to present a dropdown box which sorts these as below: What I have What I get with natsort($filelist_array); What I want (loaded into a dropdown box) <FILEID>A2011-1</FILEID> A2011-1 A2011-16 <FILEID>A2011-10</FILEID> A2011-2 A2011-15 <FILEID>A2011-11</FILEID> A2011-3 A2011-14 <FILEID>A2011-12</FILEID> A2011-4 A2011-13 <FILEID>A2011-13</FILEID> A2011-5 A2011-12 <FILEID>A2011-14</FILEID> A2011-6 A2011-11 <FILEID>A2011-15</FILEID> A2011-7 A2011-10 <FILEID>A2011-16</FILEID> A2011-8 A2011-9 <FILEID>A2011-2</FILEID> A2011-9 A2011-8 <FILEID>A2011-3</FILEID> A2011-10 A2011-7 <FILEID>A2011-4</FILEID> A2011-11 A2011-6 <FILEID>A2011-5</FILEID> A2011-12 A2011-5 <FILEID>A2011-6</FILEID> A2011-13 A2011-4 <FILEID>A2011-7</FILEID> A2011-14 A2011-3 <FILEID>A2011-8</FILEID> A2011-15 A2011-2 <FILEID>A2011-9</FILEID> A2011-16 A2011-1 <FILEID>CP2015-102 </FILEID> CP2015-75 CP2015-114 <FILEID>CP2015-106 </FILEID> CP2015-76 CP2015-106 <FILEID>CP2015-114 </FILEID> CP2015-86 CP2015-102 <FILEID>CP2015-75 </FILEID> CP2015-93 CP2015-93 <FILEID>CP2015-76 </FILEID> CP2015-102 CP2015-86 <FILEID>CP2015-86 </FILEID> CP2015-106 CP2015-76 <FILEID>CP2015-93 </FILEID> CP2015-114 CP2015-75 <FILEID>CP2016-237 </FILEID> CP2016-3 CP2016-246 <FILEID>CP2016-238 </FILEID> CP2016-237 CP2016-242 <FILEID>CP2016-241 </FILEID> CP2016-238 CP2016-241 <FILEID>CP2016-242 </FILEID> CP2016-241 CP2016-238 <FILEID>CP2016-246 </FILEID> CP2016-242 CP2016-237 <FILEID>CP2016-3 </FILEID> CP2016-246 CP2016-3 <FILEID>MC2016-164 </FILEID> MC2016-164 MC2016-167 <FILEID>MC2016-167 </FILEID> MC2016-167 MC2016-164 I load the XML file into an array as such: $FILEID = $xml->xpath('FileNOEventsDataSet/FileNOEventsData/FILEID'); $FIRST_NAT_SORT = array(); foreach ($FILEID as $node){ $FIRST_NAT_SORT[]=(string)$node[0]; } natsort($FIRST_NAT_SORT); foreach ($FIRST_NAT_SORT as $NEWFILEID){ $html .="<option value'".$NEWFILEID."' >".$NEWFILEID."</option>"; } This results in normal natsort result in the select box. What I want is column 3 above, which sorts by everything to the left of the 'dash' alphabetically, and everything to the right of the dash descending. I think I can do an explode on the $node[0], such as: $SECOND_NAT_SORT = array(); $second_nat_sort = explode("-",$node[0]); which results in $second_nat_sort[0] = 'A2001' and $second_nat_sort[1]='16' I then try to do a multiarraysort, but I am lost at this point. Above my skill level. I am extremely novice, so bear with me. Quote Link to comment Share on other sites More sharing options...
Solution requinix Posted July 20, 2016 Solution Share Posted July 20, 2016 (edited) Use usort() with strnatcmp() to implement your own sorting algorithm. usort($FIRST_NAT_SORT, function($a, $b) { $a1 = strtok($a, "-"); $a2 = strtok(""); $b1 = strtok($b, "-"); $b2 = strtok(""); return strnatcmp($a1, $b1) ?: -strnatcmp($a2, $b2); });[edit] It's been asked so I'll explain: The function to usort() has to return 0 depending on how the two arguments ($a and $b) compare to each other - negative if $a $b. Fortunately that's exactly how strnatcmp() behaves too. return strnatcmp($a1, $b1) will handle sorting for just the parts before the hyphens, but if they're equal then you need to compare the parts after the hyphens. $x ?: $y is shorthand for $x ? $x : $y, so return strnatcmp($a1, $b1) ? strnatcmp($a1, $b1) : -strnatcmp($a2, $b2);(except the shorthand will only evaluate the first strnatcmp() once) Since strnatcmp() returns 0 if the two are equal and nonzero if not, the function will return that number if the two are not equal because it won't try to do the second comparison. If they are equal then the second strnatcmp() does get evaluated. It works the exact same way as the first one, except it has a minus sign to negate the return value. Thus the result is backwards: negative if $a2 > $b2 and positive if $a2 $b2. The effect is a descending sort on the values after the hyphens. To be absolutely explicit about everything you could write that line as $compare1 = strnatcmp($a1, $b1); if ($compare1 < 0) { // $a1 < $b1 return -1; } else if ($compare1 > 0) { // $a1 > $b1 return 1; } // $compare1 == 0 and $a1 == $b1 $compare2 = strnatcmp($a2, $b2); if ($compare2 < 0) { // $a2 < $b2 return 1; // opposite result } else if ($compare2 > 0) { // $a2 > $b2 return -1; // opposite result } else { return 0; } Edited July 22, 2016 by requinix 1 Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 22, 2016 Author Share Posted July 22, 2016 Thank you!!! Worked like a charm. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 22, 2016 Author Share Posted July 22, 2016 $TABLE Should contain a list of XML elements like below. I did a print_r($TABLE) to see this result: SimpleXMLElement Object ( [DOCID] => 96629 [DATERECD] => 2016-07-14T00:00:00-04:00 [DOCTITLE] => This is title of file [FILEID] => CP2015-106 ) The $TABLE should have various rows, and then we do following: <?php foreach($TABLE as $CASE) $file_id = $CASE->FILEID; { ?> <tr> <td><a href="/file/showfile/<?php echo $file_id; ?></a></td> </tr> <?php } ?> Which produces table like below: CP2015-106 CP2015-83 CP2015-93 What I need is same sort you provided before: CP2015-106 CP2015-93 CP2015-83 Can this be done just like example you provided? I need to get the FILEID out of the SimpleXML rows and do the sort. Thoughts on this? I have one other issue in another area. Quote Link to comment Share on other sites More sharing options...
requinix Posted July 22, 2016 Share Posted July 22, 2016 If all you need is those IDs then use a foreach to extract the values into a separate array, then sort the array. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 25, 2016 Author Share Posted July 25, 2016 Your Result Needed Result A98-1 A2010-10 A99-1 A2010-9 A2000-1 A2010-8 A2003-1 A2010-7 A2006-1 A2010-6 A2007-1 A2010-5 A2009-1 A2010-4 A2010-10 A2010-3 A2010-9 A2010-2 A2010-8 A2010-1 A2010-7 A2009-1 A2010-6 A2007-1 A2010-5 A2006-1 A2010-4 A2003-1 A2010-3 A2000-1 A2010-2 A99-1 A2010-1 A98-1 Thanks again, my second question was answered. Now back to the first question on natsort. I thought the solution you provided fixed my issue, but after deeper look this is situation: Left column is what the solution you provided creates, Right column is what I am trying to do. Is it possible to sort the left side of the dash A to Z then numbers high to low natsort, and then right of dash natsort descending (10-1). Quote Link to comment Share on other sites More sharing options...
Barand Posted July 25, 2016 Share Posted July 25, 2016 (edited) try $data = [ 'A98-1', 'A99-1', 'A2000-1', 'A2003-1', 'A2006-1', 'A2007-1', 'A2009-1', 'A2010-10', 'A2010-9', 'A2010-8', 'A2010-7', 'A2010-6', 'A2010-5', 'A2010-4', 'A2010-3', 'A2010-2', 'A2010-1' ]; usort ($data, function($a, $b) { list($a1,$a2) = explode('-', $a); list($b1,$b2) = explode('-', $b); $x = strnatcmp($b1, $a1); if ($x==0) { return strnatcmp($b2,$a2); } return $x; }); Results: Array ( [0] => A2010-10 [1] => A2010-9 [2] => A2010-8 [3] => A2010-7 [4] => A2010-6 [5] => A2010-5 [6] => A2010-4 [7] => A2010-3 [8] => A2010-2 [9] => A2010-1 [10] => A2009-1 [11] => A2007-1 [12] => A2006-1 [13] => A2003-1 [14] => A2000-1 [15] => A99-1 [16] => A98-1 ) Edited July 25, 2016 by Barand 1 Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 27, 2016 Author Share Posted July 27, 2016 Array ( [0] => R2016-6 [1] => R2016-5 [2] => R2016-4 [3] => R2016-3 [4] => R2016-2 [5] => R2016-1 [6] => R2015-6 [7] => R2015-5 [8] => R2015-4 [9] => R2015-3 [10] => R2015-2 [11] => R2015-1 [12] => R2014-7 [13] => R2014-6 [14] => R2014-5 [15] => R2014-4 [16] => R2014-3 [17] => R2014-2 [18] => R2014-1 [19] => R2013-11 [20] => R2013-10 [21] => R2013-9 [22] => R2013-8 [23] => R2013-7 [24] => R2013-6 [25] => R2013-5 [26] => R2013-4 [27] => R2013-3 [28] => R2013-2 [29] => R2013-1 [30] => R2012-9 [31] => R2012-8 [32] => R2012-7 [33] => R2012-6 [34] => R2012-5 [35] => R2012-4 [36] => R2012-3 [37] => R2012-2 [38] => R2012-1 [39] => R2011-7 [40] => R2011-6 [41] => R2011-5 [42] => R2011-4 [43] => R2011-3 [44] => R2011-2 [45] => R2011-1 [46] => R2010-6 [47] => R2010-5 [48] => R2010-4 [49] => R2010-3 [50] => R2010-2 [51] => R2010-1 [52] => R2009-5 [53] => R2009-4 [54] => R2009-3 [55] => R2009-2 [56] => R2009-1 [57] => R2008-1 [58] => R2006-1 ..... [1055] => A2010-3 [1056] => A2010-2 [1057] => A2010-1 [1058] => A2009-1 [1059] => A2007-1 [1060] => A2006-1 [1061] => A2003-1 [1062] => A2000-1 [1063] => A99-1 [1064] => A98-1 [1065] => N2014-1 [1066] => N2012-2 [1067] => N2012-1 [1068] => N2011-1 [1069] => N2010-1 [1070] => N2009-1 [1071] => N2006-1 [1072] => SS2010-1 [1073] => IM2006-1 [1074] => IM2005-1 [1075] => IM2004-1 [1076] => IM2003-1 [1077] => *2003 [1078] => IM2002-1 [1079] => IM2001-1 [1080] => IM2000-1 [1081] => IM99-1 [1082] => PL105/277 [1083] => PI2016-3 [1084] => PI2016-2 [1085] => PI2016-1 [1086] => PI2015-1 [1087] => PI2014-1 [1088] => PI2013-1 [1089] => PI2012-1 [1090] => PI2010-3 [1091] => PI2010-2 [1092] => PI2010-1 [1093] => PI2009-1 [1094] => PI2008-4 [1095] => PI2008-3 [1096] => PI2008-2 [1097] => PI2008-1 [1098] => PI2007-1 [1099] => ACR2015 [1100] => ACR2014 [1101] => ACR2013 [1102] => ACR2012 [1103] => ACR2011 [1104] => ACR2010 [1105] => ACR2009 [1106] => ACR2008 [1107] => ACR2007 [1108] => CP2016-249 [1109] => CP2016-248 [1110] => CP2016-247 [1111] => CP2016-246 [1112] => CP2016-245 [1113] => CP2016-244 [1114] => CP2016-243 [1115] => CP2016-242 [1116] => CP2016-241 [1117] => CP2016-240 Ok, I tried what you have, and I get a different result. Let me see if I can explain a little better. The Array will have a pattern as such: AAA = 1 or more Letters XXXX = 1 or more numbers - = Possible Dash YYYY = 1 or more numbers and/or letters. I am trying to sort as such: AAA (1 or more letters) from A to Z. XXXX (1 or more numbers) from highest to lowest, natsort. - (May or may not be there) YYYY (Remainder of the text) would is mostly numbers. Here is a better Array which is what I have before I sort using your suggestion: Quote Link to comment Share on other sites More sharing options...
Barand Posted July 27, 2016 Share Posted July 27, 2016 Post the array again using the output from var_export($array); That gives us processable code. AAA XXXX YYYY - which bits are ASC and which bits DESC? Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 27, 2016 Author Share Posted July 27, 2016 The original array has 2000 values. What is best way to post? Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 27, 2016 Author Share Posted July 27, 2016 Attached is a text file with all values from array. Array-Sample.txt Quote Link to comment Share on other sites More sharing options...
Barand Posted July 27, 2016 Share Posted July 27, 2016 Post the array again using the output from var_export($array); Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 My bad, attached is var_export(array$); Array-Sample-Var_Export.txt Quote Link to comment Share on other sites More sharing options...
Barand Posted July 28, 2016 Share Posted July 28, 2016 Thanks, saves a lot of editing Now which bits are ascending and which are descending in the required sort? Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 This is a weird sort. The first set of letters would be A to Z. The next set of numbers would be from high to low in natural sort. Then anything after the dash would be natural sort, high to low. AC2009-1 ACR2016-10 ACR2016-9 etc.... Quote Link to comment Share on other sites More sharing options...
Barand Posted July 28, 2016 Share Posted July 28, 2016 how's this? usort($data, function($a,$b) { list($a1,$a2,$a3) = splitParts($a); list($b1,$b2,$b3) = splitParts($b); $x = strcmp($a1,$b1); if ($x == 0) { $y = $b2 - $a2; if ($y == 0) { return $b3 - $a3; } return $y; } return $x; }); function splitParts($s) { $k = strlen($s); $a = ['','','']; $i = 0; $p = 0; while ($i < $k) { $c = $s[$i++]; if (ctype_digit($c) && $p==0) $p = 1; if ($c=='-') { $c = $s[$i++]; ++$p; } $a[$p] .= $c; } return $a; } Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 Something must be wrong in syntax, nothing comes up. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 Attached is the full set of code. Must be something wrong is the syntax on my end. Fill-ID.txt Quote Link to comment Share on other sites More sharing options...
Barand Posted July 28, 2016 Share Posted July 28, 2016 here's mine, with the data you sent weirdsort.php Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 Ok, as I said, I am new to PHP. I had to move the function you provided out of the function I placed it in. Working now. Let me look at results in a few places and confirm. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 28, 2016 Author Share Posted July 28, 2016 I just had wrong code in wrong place. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 29, 2016 Author Share Posted July 29, 2016 Ok, this all worked in the area I needed. Thank you endlessly for that. Now I have another section of code, that does not seem to work. It is the same principle but the code runs elsewhere. When I place your function in the code, it fails. This is must be inline php issue or something. Attached is the var_dump and var_export of the data. It must be something different, as I can not seem to get the code to work on this data set. Array-Sample2-varExport.txt Array-Sample2-varDump.txt Quote Link to comment Share on other sites More sharing options...
Barand Posted July 29, 2016 Share Posted July 29, 2016 Given that the structure of the new array is nothing like the structure of the original, it is hardly surprising that it doesn't work. The array also contains references to undocumented SimpleXMLElement::__set_state() method. Fatal error: Call to undefined method SimpleXMLElement::__set_state() Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 29, 2016 Author Share Posted July 29, 2016 I'm with you on that... I'm thinking it can somehow be loaded into a regular array, sorted and then used as normal. Quote Link to comment Share on other sites More sharing options...
lemaster777 Posted July 29, 2016 Author Share Posted July 29, 2016 http://php.net/manual/en/book.simplexml.php Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.