Jump to content

WireArray sort() and find() fixes

Recommended Posts

A recent post on PageArray find() made me try and fix a couple of things discovered there (and in some earlier posts, I think). What I came up with is a few modifications to WireArray class (base class for PageArrays):

  • sort() accepts multiple fields to sort by. Fields can be given as a comma separated string "-modified, title" or an array of strings array("-modified", "title")
  • find() supports start=n and multiple sort=field selectors
  • some optimizations for sorting and limiting even quite big arrays without losing performance in certain conditions

The new implementation seems to give same results as the old one where applicable (given only one field to sort by and no start selector for find()), but more tests are definitely needed. I've tested this on a site with more than 15000 pages and find()'ing 3000 of them at once for sorting/finding with the API calls. Performance is about the same as it was before. Even giving more than one field to sort by doesn't make it noticeably slower if limit is being used as well (those optimizations actually work).

The replacement for wire/core/Array.php is attached if anyone's interested in trying it out.


I tried some different methods of sorting as well, but just to find out Ryan had a good reason for all the little things I couldn't get a hold of at first :). PHP sorting methods being unstable must have turned much of my hair gray in the last couple of days...

While testing this I found out that sorting by a field with empty values at some records would act differently when done at initial $pages->find() (database query that is) compared to $aPageArray->find(). While this isn't affected by the WireArray code, I may dig deeper into it later.

Giving sort=random works as it did before, but the code doesn't handle the situation it's not the only sort field given. Left a TODO-note in the comments on this (and the missing trackChange() call as well).

@ryan: I will try to make a pull request as well just for the fun of it (being new to git). Do whatever you like with the code if you see any potential in it. You may want to rename function stableSort() and its last argument to something else at least. I know I'm not too happy with them.

  • Like 6

Share this post

Link to post
Share on other sites

Just got your pull request -- this is great, thanks for taking the time to help out with this!

  • Like 1

Share this post

Link to post
Share on other sites

Thanks, it was my pleasure. And a bit pain as well, but mostly pleasure. :)

  • Like 3

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Spyros
      I'm having a strange issue with the $page->find(), for some reason I'm missing some of the pages from the results. I found then that I was missing all the pages with the same "PAGE NAME". Is it a bug or am I missing something?
      If I change the "PAGE NAME" of one of the missing ones then I'm retrieving the page without any problem.
      Thank you
    • By ngrmm
      I want to have filters with month names in german. I fetch them from date-fields with strftime('%B', $timestamp);
      But i'm not able to add them correctly to a wireArray()
      What's the right way to do that?
      $ms = wireArray(); $m1 = strftime('%B', 1579643232); // Januar $m2 = strftime('%B', 1583107200); // März $ms->prepend($m1); $ms->prepend($m2); foreach($ms as $name) { echo "$name "; // result: Januar M�rz }  
    • By celfred
      Still in my 'teaching game'. Here's my problem : I 'find' all players with a request like

      $allPlayers = $pages->find("template=player"); [/code Then, I limit to players belonging to the team of the logged in player with [code] $teamPlayers = $allPlayers->find("team=$loggedPlayer->team");
      No problem so far. But my scoreboards rely on either :


      to find the logged player's position in the charts.
      On the 'global' newboard with scoreboards based upon $allPlayers, everything works as expected.
      BUT on my 'team' newsboard, even though I'm using $teamPlayers, the returned indexes are based upon $allPlayers. Am I clear ? In other words, I have a total of 125 players, and my logged player is 61 out of 125 regarding the number of places he freed. But in his particular team of 25 players, he sould be 15 whereas he's still 61
      I'd like to reset my indexes (and start back from 0), but I can't find my way out of this...
      If someone has a hint to help, I'd appreciate.
      I have a second part in my worry : I had a way around it by simply making another 'raw' request :

      $teamPlayers = $pages->find("team=$loggedPlayer->team");
      Then my team indexes were right, but I faced another issue : Reordering my wirearray according to the scoreboard I want usually worked fine (simple sort() based upon an integer field, for example, player's coins, player's karma...) and indexes were updated BUT resorting with places.count ('places' field is a pageArray) doesn't update the indexes returned by getItemKey and my logged player is always at the position he was when I first did my initial $pages->find() query  
      So my way around found its limit and that's why I'm posting here, after struggling with this for a couple of hours...
      Thanks in advance for the help. 
    • By SwimToWin
      While ProcessWire and WireArray does not have support for array_chunk, there is a simple way to achieve this.
      With array_chunk() you can easily add DIVs to a foreach loop, without having to set up counters when using general PHP (hat-tip to Laurance over at StackOverflow). The idea in a ProcessWire context is to use array_chunk() to split an array into chunks - and use eq() when looping page results.
      Simple example that will split a WireArray into three columns. Before we begin, you should know the array_chunk syntax: array_chunk($array, $chunk_size, $preserve_keys=true|false).
      <?php $p = $pages->get('/news')->children('limit=15, template=article, sort=-sort'); ?> <div class="row"> <?php foreach (array_chunk(range(0,14),5) as $chunk): ?> <div class="col"> <?php foreach ($chunk as $i): ?> <h5><a href="<?=$p->eq($i)->url?>"><?=$p->eq($i)->title?></a></h5> <?php endforeach; ?> </div> <?php endforeach; ?> </div> A more realistic example:
      <?php $p = $pages->get('/news'); $pp = $p->children('limit=15, template=article, sort=-sort'); ?> <h2><a href="<?=$p->url?>"><?=$p->title?></a></h2> <div class="row"> <?php foreach (array_chunk(range(0,14),5) as $chunk): ?> <div class="col"> <?php foreach ($chunk as $i): ?> <h5> <a href="<?=$pp->eq($i)->url?>"><?=$pp->eq($i)->title?></a> </h5> <?php endforeach; ?> </div> <?php endforeach; ?> </div>
    • By kongondo
      In a module-context, as we know, stuff like single file or image fields return 'arrays', i.e. become iterable. This means that directly using WireArray::Iterable() to check whether a field is iterable can be misleading. Other than to temporarily set a page's output formatting to true (still within a module context), checking iterability of a field on the page, then reverting the page's output formatting to false after the check, is there another/a better way to achieve the same feat? Thanks.
  • Create New...