Jump to content

Filtering / Type / Array issues


celfred
 Share

Recommended Posts

Hello, 

I've been struggling for hours with this, and it is driving me crazy. I guess I completely misunderstand basic concepts, but I'm facing a wall here hence this message... Here's what I'm trying to do :

  • I have a list of players in $allPlayers (pages)
  • Each player has (among others) 2 pageArray fields : 'group' and 'team'.
  • I'm trying to loop through all the players to build another pageArray (or WireArray or whatever...) to gather the players in groups/teams accordingly and deal with the different info I have on them and calculate a score.
  • My main problem is that some players are in a different team but their group has the same name (and I need to restrict players to their own team mates).

Here's my 'code' so far (which doesn't work) :

    // Build group list (for players having a team)
    $allPlayers->filter("team.name!=no-team")->sort("team.name");
    $allGroups = new PageArray();
    $uniqueGroups = [];
	$uGroups = new PageArray(); // This was a test, see below...
    $already = [];
    foreach($allPlayers as $p) {
      // Assign a groupId to each player
      $groupId = $p->team->id.$p->group->id;
      $p->groupId = $groupId;
      if (!in_array($groupId, $already)) {
        // The following lines were a test but didn't work and I DON'T UNDERSTAND WHY ?
		// $uG = new Page();
        // $uG->of(false);
        // $uG->template = 'player';
        // $uG->name = $groupId;
        // $uGroups->add($uG);
		// bd($uGroups->count()); // !! Keeps showing 1 ? My purpose was to be able to use $uGroups->karma, $uGroups->nbBonus... afterwards
        array_push($uniqueGroups, $groupId);
      }
      array_push($already, $groupId);
    }
 
	// Then, I loop through $uniqueGroups, get the players and calulate my score
    foreach($uniqueGroups as $group) {
      // Limit to groupId players for group scores
      $players = $allPlayers->find("groupId=$group");
      // Check for group bonus and calculate scores
		[...] // I cut to simplify my post
		// But my other problem is storing my score : I can't use $group->karma or $group->nbBonus since $group is a ragular PHP array ?
     }

	// Then, I wanted to create a new pageArray (hence my $uGroups above) to be able to sort them with the following
	$uGroups->sort("-karma");

And I'm stuck... More than 5 hours today already on this, and still blocked:(

I've tried playing with WireData, WireArray... but I'm all lost here. You can understand why I'm posting in the 'Getting started' forum !

For further information, this part of code is embedded in a setScoreboard() function and this is the 'group' part of it.

If anyone can devote a little time to give me a clue on how to think about this, I'd greatly appreciate !

Thanks !

PS : The more I write about this, the more I think I am able to get the group members and scores (with my awkward coding), but then I am unable of simply storing this information before rendering it. Here I'm referring to my comment in my code where I say that TracyDebugger keeps showing me only 1 $uGroups page. I wish I could dynamically build a 'uniqueGroup' page with Pagefields for Group, Team, pageArray for Members, Integer fields for calculated Karma, nbBonus...... sigh......  

Link to comment
Share on other sites

9 hours ago, celfred said:

I'm trying to loop through all the players to build another pageArray (or WireArray or whatever...) to gather the players in groups/teams accordingly and deal with the different info I have on them and calculate a score.

I think you'll find it easier if you use a plain PHP array so that you can make it nested/multidimensional. You can't have nested PageArrays you see.

I'm not sure I 100% understand your scenario, but hopefully this helps:

// Get $allPlayers somehow

$uniqueGroups = [];
foreach($allPlayers as $player) {
    // Define the $groupId
    // Use an underscore (or other non-integer) in the $groupId to force it to be a string, not an integer
    // That way when array_multisort() is used later the keys will be preserved
    $groupId = $player->team->id . '_' . $player->group->id;
    // Optional: add $groupId to $player as a custom property in case you need it later
    $player->groupId = $groupId;
    // Add player to $uniqueGroups using $groupId as key
    $uniqueGroups[$groupId]['players'][] = $player;
}
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

foreach($uniqueGroups as $groupId => $data) {
    $uniqueGroups[$groupId]['karma'] = 0; // initialise to zero
    $uniqueGroups[$groupId]['bonus'] = 0; // initialise to zero
    // Tally up the group's karma
    foreach($data['players'] as $player) {
        $uniqueGroups[$groupId]['karma'] += $player->karma;
    }
    // Do the same for bonus
    //...
}
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

// Sort the $uniqueGroups by karma (for example)
array_multisort(array_map(function($group) {
    return $group['karma']; // return the value you want to sort by
}, $uniqueGroups), SORT_ASC, $uniqueGroups);
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

See this article for a good introduction to array_multisort() - in the code above array_map() is used to get the sort values instead of another foreach().

Edited by Robin S
Added code for sorting $uniqueGroups
  • Like 1
Link to comment
Share on other sites

Hello,

 

First of all, THANKS a lot for your answer which helps me better understand things. I'll go ahead and dig what you're mentionning with 'multisort'. It seems quite interesting and I have a feeling it fills a big lack in my knowledge ;) 

In the meantime, I have managed to do some stuff yesterday night which seems to do the job. In case you have a little time to read through it and see things I shouldn't do, I would be glad to have some kind of feedback on that (but as I said, I'll test your code above during the day). Roughly speaking, if I want the 'group' scoreboard, I loop through 'allPlayers' to get a list of groups and teams which I add to PageArrays. Then, I loop through all groups, get involved players and (here the thing !) I clone the group page to add it to a $uniqueGroup pageArray. And this page holds the calculated 'bonus' and 'karma'. Then, I do my thing with the 'reindex' (see code below) to try and get the logged in player's position and return all my variables to be able, then, to display a particular scoreboard (top 10, top 3 + 'surrounded' player...)

I know my explanations are hard to follow. Here's the code :

function setScoreboard($allPlayers, $player, $field) {
  if ($field != 'group') {
    $allPlayers->sort($field);
  } else {
    $allPlayers->filter("team.name!=no-team")->sort("team.name");
    $allGroups = new PageArray();
    $allTeams = new PageArray();
    $already = [];
    $uniqueGroups = new PageArray();
    foreach($allPlayers as $p) {
      $groupId = $p->team->id.$p->group->id;
      $p->groupId = $groupId;
      if (!in_array($groupId, $already)) {
        $allGroups->add($p->group);
        $allTeams->add($p->team);
      }
      array_push($already, $groupId);
    }
    foreach($allGroups as $group) {
      $index = 0;
      foreach($allTeams as $team) {
        $players = $allPlayers->find("group=$group, team=$team");
        if ($players->count() > 0) {
          $newGroup = clone $group;
          $newGroup->team = $team;
          $newGroup->id = $group->id.$team->id;
          $newGroup->focus = 0;
          // Check for group bonus
          $newGroup->nbBonus = groupBonus($players);
          $newGroup->yearlyKarma = $newGroup->nbBonus*30;
          // Add individual karmas
          foreach($players as $p) {
            // Karma is divided by number of players in the newGroup to be fair with smaller newGroups
            $newGroupYearlyKarma = round($p->yearlyKarma/$players->count);
            (int) $newGroup->yearlyKarma = $newGroup->yearlyKarma + $newGroupYearlyKarma;
            $newGroup->details .= $p->title.' ';
            if ($p->name == $player->name) {
              $newGroup->focus = 1;
            }
          }
          $uniqueGroups->add($newGroup);
          $index++;
        }
      }
    }
    $allPlayers = $uniqueGroups->sort('-yearlyKarma');
  }
  $allPlayersReindexed = new PageArray();
  $allPlayersReindexed->import($allPlayers);
  if ($player) {
    if ($field != 'group') {
      $playerPos = $allPlayersReindexed->getItemKey($player)+1;
    } else {
      $playerGroup = $allPlayersReindexed->get("focus=1");
      $playerPos = $allPlayersReindexed->getItemKey($playerGroup)+1;
    }
  } else {
    $playerPos = false;
  }
  if ($playerPos) { // Player is in the charts
    if ($playerPos <= 5) { // Good position, get Top 6
      $topPlayers = $allPlayersReindexed->slice(0, 6);
      $prevPlayers = false;
    } else {
      $topPlayers = $allPlayersReindexed->slice(0, 3); // Get top 3 players
      $prevIndex = $playerPos-3;
      $prevPlayers = $allPlayersReindexed->slice($prevIndex, 4);
    }
  } else { // No ranking (or complete scoreboard)
    $topPlayers = $allPlayersReindexed->slice(0, 10); // Get Top 10
    $prevPlayers = false;
  }
  return array($topPlayers, $prevPlayers, $playerPos, $allPlayersReindexed->count);
}

If you want to have a look of what is the final page, you can see there (on that page, I use Ajax requests to load all scoreboards individually (and the 'group' scoreboard is actually not functionnning well), but I intended to get rid of this server load since my $allPlayers is already loaded in my head.inc, I imagined optimizing my page by simply managing my scoreboards in PHP functions. 

Anyway, thanks again for your help ! And feel free to give me advice if you see horrors in my code :) (but again, I'll take the time to analyze the solution in @Robin S's answer)

Link to comment
Share on other sites

Still trying to understand things better. I've been looking at @Robin S's answer and I'm facing another issue. To set my nbBonus for each group, I had this function :

function groupBonus($players) {
  $nbBonus = 0;
  // Sort players by nb of places
  $players->sort('places.count');
  // Get min/max nb of places in the group
  $min = $players->first()->places->count;
  $max = $players->last()->places->count;
  if ($min == 0) { // 1 player has 0 places, so NO bonus possible
    return 0; 
  } else { // No player has 0 places, let's check if they all have 1,2,3... places
    for ($i=1; $i<=$min; $i++) {
      $nbPlaces = $players->find("places.count>=$i")->count;
      if ($nbPlaces == $players->count) {
        $nbBonus++;
      }
    }
  }
  return $nbBonus;
}

So my parameter $players was a PageArray() I had thanks to a 'find' request. But now, it is a regular PHP array and this throws an error because I'm trying to use lines such as $players->sort('places.count')...

All in all, I was trying to rework my function to work on regular PHP arrays, but then I have other headaches because I use many PW ways afterwards and I eventually wondered if I could simply turn my $players regular PHP array into a pageArray() again ?

And eventually, I wonder what you feel seems to be the best way for me ? Would it be preferable to work on regular PHP arrays all through my functions or should I 'convert' as soon as possible (if possible) so I can use PW way (or what I understand being PW way...)

And if you can give me a hint on what I should read more about (at this point) concerning my programming skills, I'd appreciate as well because I see that my skills are getting too low for what I want to do on my project ;)  

Thanks !

Link to comment
Share on other sites

@celfred, I don't think I'll be able to get my head around all the facets of your PW-based game, so will just answer in general terms.

First thing is that, broadly speaking, there's no reason to avoid using plain PHP arrays, functions, etc in your development if that is easier or clearer for whatever you're wanting to achieve. Behind the scenes PW will be using these native PHP types and functions anyway. So it comes down to choosing whatever you find easiest or most transparent.

So by all means use WireArrays/PageArrays where you find that easiest - in most circumstances that will be the way to go. But in your code I saw some places where you are creating new Page objects that are not actually going to be saved in the page tree anywhere. As if you are just creating these pages as a way to temporarily store some data. That doesn't make much sense to me and leads me to think there is probably some better way to do it.

And it also pays to think about creating some structure to your data so that if you need to debug it or come back to the code in six months time you can dump things and understand what is going on from the dump. So that's why I suggested using a PHP array where the key names and array structure reveal the purpose of what you're doing.

But if you need to take the 'players' element and do other things with it in the PW API then you're right that it would be easier if it were a PageArray rather than a plain PHP array of Page objects. So you could make that element a PageArray and then add pages to it...

//...
foreach($allPlayers as $player) {
    // Define the $groupId
    // Use an underscore (or other non-integer) in the $groupId to force it to be a string, not an integer
    // That way when array_multisort() is used later the keys will be preserved
    $groupId = $player->team->id . '_' . $player->group->id;
    // Optional: add $groupId to $player as a custom property in case you need it later
    $player->groupId = $groupId;
    // Add player to a PageArray in $uniqueGroups using $groupId as key
    if(!isset($uniqueGroups[$groupId]['players'])) $uniqueGroups[$groupId]['players'] = new PageArray();
    $uniqueGroups[$groupId]['players']->add($player);
}
//...

Then when you later need to work with the 'players' element it is a PageArray that you can use methods like sort() on.

 

And while looking into the issue I discovered something new: although you cannot create a nested PageArray, you can create a WireArray of PageArrays. So you could think about taking this approach if you are more comfortable sticking with PW API methods rather than doing sorting etc in plain PHP.

An example of what I mean about a WireArray of PageArrays...

// Create a new WireArray to hold the PageArrays
$my_wirearray = new WireArray();

// PageArray number 1
$pa1 = $pages->find("template=foo");

// PageArray number 2
$pa2 = $pages->find("template=bar");

// PageArray number 3
$pa3 = $pages->find("template=baz");

// Attach some custom data to the PageArrays
$pa1->data('karma', 25);
$pa2->data('karma', 13);
$pa3->data('karma', 37);

// Add the PageArrays to the WireArray
$my_wirearray->add($pa1);
$my_wirearray->add($pa2);
$my_wirearray->add($pa3);

// If you need to sort the WireArray by the custom data
$my_wirearray->sort('-karma');

// Or maybe you want to explode on that custom data
$karma_values = $my_wirearray->explode('karma', ['getMethod' => 'data']);

 

  • Like 2
Link to comment
Share on other sites

Hello,

First of all, thanks a lot for your great answer. You took the the time to post nice explanations and this will help me a lot to improve.

Quote

I don't think I'll be able to get my head around all the facets of your PW-based game, so will just answer in general terms.

No problem of course. This is exactly what I expected : general comments about some newbie mistakes you can quickly notice in my code.

Quote

First thing is that, broadly speaking, there's no reason to avoid using plain PHP arrays, functions, etc in your development if that is easier or clearer for whatever you're wanting to achieve. Behind the scenes PW will be using these native PHP types and functions anyway. So it comes down to choosing whatever you find easiest or most transparent.

Well, maybe this sounds obvious to a a lot of readers, but I guess I needed to be reminded of that advice :) 

On 4/30/2018 at 2:15 AM, Robin S said:

But in your code I saw some places where you are creating new Page objects that are not actually going to be saved in the page tree anywhere. As if you are just creating these pages as a way to temporarily store some data. That doesn't make much sense to me and leads me to think there is probably some better way to do it.

I do that sometimes because I feel better this way. I'll re-read my code and try to think differentely in the future by applying what you explain afterwards.

Quote

An example of what I mean about a WireArray of PageArrays...

Thanks for your nice example. It is very much inspiring to me.

I feel like understanding things better thanks to your reply so again, thanks a lot @Robin S for your help !

It's not always easy to learn coding by yourself and every time I end up asking the PW community for help, I get a nice answer that cheers me up and makes me understand things better. That's really cool !  

  • Like 1
Link to comment
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
 Share

×
×
  • Create New...