Related
i'm using the api from leaguepedia to receive the informations.
So I have this code to show how many times a champion was picked in a tournament, its working fine, but
right now im trying to show the winrate and i'm having problems,
for example :
Syndra picked 4 Times - Won 3 times - winrate = 75%, thats the result i expect;
Gragas picked 3 Times - Won 3 times - winrate = 100%, thats the result i expect;
what im recieving is :
Syndra picked 4 Times - Won 1 time - winrate = 25%
Gragas picked 3 Times - Won 1 time - winrate = 33,33% , victory is showing 1 for every champion (image)
i know that my problem might be on the "switch / case", but i don't know how to fix
so how can i fix my code to show the properly win rate.
thanks
thats my code
<?php
// $Result is a CURL coming from leaguepedia api
$result = json_decode($file_contents);
$heroPicks = [];
$heroVictory = [];
// Double foreach to access the values from leaguepedia api
foreach ($result as $d) {
foreach ($d as $data) {
//$data->title->Team1Picks and
// $data->title->Team2Picks is coming from leaguepedia api
//as a string separated by "," Ex:("Gragas,Shen,Maokai,etc")
// So i need to explode and merge to an array to count.
$picks1 = explode(",", $data->title->Team1Picks);
$picks2 = explode(",", $data->title->Team2Picks);
$picks = array_merge($picks1, $picks2);
// this switch is to check if
// $data->title->Winner == 1 , it means that
// $picks1 won the game else == 2 $picks2
// won the game ($data->title->Winner is coming from api aswell)
switch ($data->title->Winner) {
case 1:
$w = $picks1;
break;
case 2:
$w = $picks2;
break;
}
//foreach to count the times a champion was picked
foreach ($picks as $pick) {
$pick = trim($pick);
if (!array_key_exists($pick, $heroPicks)) {
$heroPicks[$pick] = 0;
}
$heroPicks[$pick] += 1;
}
//foreach to count how many times a champion won a game
foreach ($w as $victory) {
$victory = trim($victory);
if (!array_key_exists($victory, $heroVictory)) {
$heroVictory[$victory] = 0;
}
$heroVictory[$victory] += 1;
}
}
}
//sorting the arrays
uasort(
$heroPicks,
function ($a, $b) {
return $a - $b;
}
);
uasort(
$heroVictory,
function ($c, $d) {
return $c - $d;
}
);
$heroPicks = array_reverse($heroPicks);
$heroVictory = array_reverse($heroVictory);
//foreach to show the results
echo "Best picks:" . PHP_EOL . "<br />";
foreach ($heroPicks as $heroName => $pickCount) {
foreach ($heroVictory as $heroVictoryName => $totalVictory) {
$total = ($totalVictory * 100) / $pickCount;
}
echo $heroName . " - " . $pickCount . PHP_EOL . " - Victorys = " . $totalVictory . " -- winrate :" . $total . "%" . "<br />";
}
?>
the $result variable for #bassxzero
The outer foreach will loop through all of your heroes that were picked. This is what you want.
The inner foreach is looping through all of your heroes' victories, regardless of the hero the outer loop is currently processing. All the victories are set to 1 because that is how many victories the last hero in your victory array has. This is not what you want.
You don't want a second inner foreach. You just want to lookup the victory stats for the hero currently being processed by the outer foreach.
foreach ($heroPicks as $heroName => $pickCount) {
$totalVictory = $heroVictory[$heroName] ?? 0;
$total = ($totalVictory * 100) / $pickCount;
echo $heroName . " - " . $pickCount . PHP_EOL . " - Victorys = " . $totalVictory . " -- winrate :" . $total . "%" . "<br />";
}
Example Data
For this question, let's assume the following items:
Items: Apple, Banana, Carrot, Steak, Onion
Values: 2, 2, 4, 5, 3
Weights: 3, 1, 3, 4, 2
Max Weight: 7
Objective:
The MCKP is a type of Knapsack Problem with the additional constraint that "[T]he items are subdivided into k classes... and exactly one item must be taken from each class"
I have written the code to solve the 0/1 KS problem with dynamic programming using recursive calls and memoization. My question is whether it is possible to add this constraint to my current solution? Say my classes are Fruit, Vegetables, Meat (from the example), I would need to include 1 of each type. The classes could just as well be type 1, 2, 3.
Also, I think this can be solved with linear programming and a solver, but if possible, I'd like to understand the answer here.
Current Code:
<?php
$value = array(2, 2, 4, 5, 3);
$weight = array(3, 1, 3, 4, 2);
$maxWeight = 7;
$maxItems = 5;
$seen = array(array()); //2D array for memoization
$picked = array();
//Put a dummy zero at the front to make things easier later.
array_unshift($value, 0);
array_unshift($weight, 0);
//Call our Knapsack Solver and return the sum value of optimal set
$KSResult = KSTest($maxItems, $maxWeight, $value, $weight);
$maxValue = $KSResult; //copy the result so we can recreate the table
//Recreate the decision table from our memo array to determine what items were picked
//Here I am building the table backwards because I know the optimal value will be at the end
for($i=$maxItems; $i > 0; $i--) {
for($j=$maxWeight; $j > 0; $j--) {
if($seen[$i][$j] != $seen[$i-1][$j]
&& $maxValue == $seen[$i][$j]) {
array_push($picked, $i);
$maxValue -= $value[$i];
break;
}
}
}
//Print out picked items and max value
print("<pre>".print_r($picked,true)."</pre>");
echo $KSResult;
// Recursive formula to solve the KS Problem
// $n = number of items to check
// $c = total capacity of bag
function KSTest($n, $c, &$value, &$weight) {
global $seen;
if(isset($seen[$n][$c])) {
//We've seen this subproblem before
return $seen[$n][$c];
}
if($n === 0 || $c === 0){
//No more items to check or no more capacity
$result = 0;
}
elseif($weight[$n] > $c) {
//This item is too heavy, check next item without this one
$result = KSTest($n-1, $c, $value, $weight);
}
else {
//Take the higher result of keeping or not keeping the item
$tempVal1 = KSTest($n-1, $c, $value, $weight);
$tempVal2 = $value[$n] + KSTest($n-1, $c-$weight[$n], $value, $weight);
if($tempVal2 >= $tempVal1) {
$result = $tempVal2;
//some conditions could go here? otherwise use max()
}
else {
$result = $tempVal1;
}
}
//memo the results and return
$seen[$n][$c] = $result;
return $result;
}
?>
What I've Tried:
My first thought was to add a class (k) array, sort the items via class (k), and when we choose to select an item that is the same as the next item, check if it's better to keep the current item or the item without the next item. Seemed promising, but fell apart after a couple of items being checked. Something like this:
$tempVal3 = $value[$n] + KSTest($n-2, $c-$weight[$n]);
max( $tempVal2, $tempVal3);
Another thought is that at the function call, I could call a loop for each class type and solve the KS with only 1 item at a time of that type + the rest of the values. This will definitely be making some assumptions thought because the results of set 1 might still be assuming multiples of set 2, for example.
This looks to be the equation (If you are good at reading all those symbols?) :) and a C++ implementation? but I can't really see where the class constraint is happening?
The c++ implementation looks ok.
Your values and weights which are 1 dimensional array in your current PHP implementation will become 2 dimensional.
So for example,
values[i][j] will be value of j th item in class i. Similarly in case of weights[i][j]. You will be taking only one item for each class i and move forward while maximizing the condition.
The c++ implementation also does an optimization in memo. It only keeps 2 arrays of size respecting the max_weight condition, which are current and previous states. This is because you only need these 2 states at a time to compute present state.
Answers to your doubts:
1)
My first thought was to add a class (k) array, sort the items via
class (k), and when we choose to select an item that is the same as
the next item, check if it's better to keep the current item or the
item without the next item. Seemed promising, but fell apart after a
couple of items being checked. Something like this: $tempVal3 =
$value[$n] + KSTest($n-2, $c-$weight[$n]); max( $tempVal2, $tempVal3);
This won't work because there could be some item in class k+1 where you take a optimal value and to respect constraint you need to take a suboptimal value for class k. So sorting and picking the best won't work when the constraint is hit. If the constraint is not hit you can always pick the best value with best weight.
2)
Another thought is that at the function call, I could call a loop for
each class type and solve the KS with only 1 item at a time of that
type + the rest of the values.
Yes you are on the right track here. You will assume that you had already solved for first k classes. Now you will try extending using the values of k+1 class respecting the weight constraint.
3)
... but I can't really see where the class constraint is happening?
for (int i = 1; i < weight.size(); ++i) {
fill(current.begin(), current.end(), -1);
for (int j = 0; j < weight[i].size(); ++j) {
for (int k = weight[i][j]; k <= max_weight; ++k) {
if (last[k - weight[i][j]] > 0)
current[k] = max(current[k],
last[k - weight[i][j]] + value[i][j]);
}
}
swap(current, last);
}
In the above c++ snippet, the first loop iterates on class, the second loop iterates on values of class and the third loop extends the current state current using the previous state last and only 1 item j with class i at a time. Since you are only using previous state last and 1 item of the current class to extend and maximize, you are following the constraint.
Time complexity:
O( total_items x max_weight) which is equivalent to O( class x max_number_of_items_in_a_class x max_weight)
So I am not a php programmer but I will try to write a pseudocode with good explanation.
In the original problem each cell i, j meaning was: "Value of filling the knapsack with items 1 to i until it reach capacity j", the solution in the link you have provided defines each cell as "Value of filling the knapsack with items from buckets 1 to i until it reach capacity j". Notice that in this variation there is not such this as not taking an element from a class.
So on each step (each call for KSTest with $n, $c), we need to find which element to pick from the n'th class such that the weight of this element is less than c and it's value + KSTest(n - 1, c - w) is the greatest.
So I think you should only change the else if and else statements to something like:
else {
$result = 0
for($i=0; $i < $number_of_items_in_nth_class; $i++) {
if ($weight[$n][$i] > $c) {
//This item is too heavy, check next item
continue;
}
$result = max($result, KSTest($n-1, $c - $weight[$n][$i], $value, $weight));
}
}
Now two disclaimers:
I do not code in php so this code will not run :)
This is not the implementation given in the link you provided, TBH I didn't understood why the time complexity of their algorithm is so small (and what is C) but this implementation should work since it is following the definition of the recursive formula given.
The time complexity of this should be O(max_weight * number_of_classes * size_of_largerst_class).
This is my PHP solution. I've tried to comment the code in a way that it's easy to follow.
Update:
I updated the code because the old script was giving unreliable results. This is cleaner and has been thoroughly tested. Key takeaways are that I use two memo arrays, one at the group level to speed up execution and one at the item level to reconstruct the results. I found any attempts to track which items are being chosen as you go are unreliable and much less efficient. Also, isset() instead of if($var) is essential for checking the memo array because the previous results might have been 0 ;)
<?php
/**
* Multiple Choice Knapsack Solver
*
* #author Michael Cruz
* #version 1.0 - 03/27/2020
**/
class KS_Solve {
public $KS_Items;
public $maxValue;
public $maxWeight;
public $maxItems;
public $finalValue;
public $finalWeight;
public $finalItems;
public $finalGroups;
public $memo1 = array(); //Group memo
public $memo2 = array(); //Item memo for results rebuild
public function __construct() {
//some default variables as an example.
//KS_Items = array(Value, Weight, Group, Item #)
$this->KS_Items = array(
array(2, 3, 1, 1),
array(2, 1, 1, 2),
array(4, 3, 2, 3),
array(5, 4, 2, 4),
array(3, 2, 3, 5)
);
$this->maxWeight = 7;
$this->maxItems = 5;
$this->KS_Wrapper();
}
public function KS_Wrapper() {
$start_time = microtime(true);
//Put a dummy zero at the front to make things easier later.
array_unshift($this->KS_Items, array(0, 0, 0, 0));
//Call our Knapsack Solver
$this->maxValue = $this->KS_Solver($this->maxItems, $this->maxWeight);
//Recreate the decision table from our memo array to determine what items were picked
//ksort($this->memo2); //for debug
for($i=$this->maxItems; $i > 0; $i--) {
//ksort($this->memo2[$i]); //for debug
for($j=$this->maxWeight; $j > 0; $j--) {
if($this->maxValue == 0) {
break 2;
}
if($this->memo2[$i][$j] == $this->maxValue
&& $j == $this->maxWeight) {
$this->maxValue -= $this->KS_Items[$i][0];
$this->maxWeight -= $this->KS_Items[$i][1];
$this->finalValue += $this->KS_Items[$i][0];
$this->finalWeight += $this->KS_Items[$i][1];
$this->finalItems .= " " . $this->KS_Items[$i][3];
$this->finalGroups .= " " . $this->KS_Items[$i][2];
break;
}
}
}
//Print out the picked items and value. (IMPLEMENT Proper View or Return!)
echo "<pre>";
echo "RESULTS: <br>";
echo "Value: " . $this->finalValue . "<br>";
echo "Weight: " . $this->finalWeight . "<br>";
echo "Item's in KS:" . $this->finalItems . "<br>";
echo "Selected Groups:" . $this->finalGroups . "<br><br>";
$end_time = microtime(true);
$execution_time = ($end_time - $start_time);
echo "Results took " . sprintf('%f', $execution_time) . " seconds to execute<br>";
}
/**
* Recursive function to solve the MCKS Problem
* $n = number of items to check
* $c = total capacity of KS
**/
public function KS_Solver($n, $c) {
$group = $this->KS_Items[$n][2];
$groupItems = array();
$count = 0;
$result = 0;
$bestVal = 0;
if(isset($this->memo1[$group][$c])) {
$result = $this->memo1[$group][$c];
}
else {
//Sort out the items for this group
foreach($this->KS_Items as $item) {
if($item[2] == $group) {
$groupItems[] = $item;
$count++;
}
}
//$k adjusts the index for item memoization
$k = $count - 1;
//Find the results of each item + items of other groups
foreach($groupItems as $item) {
if($item[1] > $c) {
//too heavy
$result = 0;
}
elseif($item[1] >= $c && $group != 1) {
//too heavy for next group
$result = 0;
}
elseif($group == 1) {
//Just take the highest value
$result = $item[0];
}
else {
//check this item with following groups
$result = $item[0] + $this->KS_Solver($n - $count, $c - $item[1]);
}
if($result == $item[0] && $group != 1) {
//No solution with the following sets, so don't use this item.
$result = 0;
}
if($result > $bestVal) {
//Best item so far
$bestVal = $result;
}
//memo the results
$this->memo2[$n-$k][$c] = $result;
$k--;
}
$result = $bestVal;
}
//memo and return
$this->memo1[$group][$c] = $result;
return $result;
}
}
new KS_Solve();
?>
(Working on a search algorithm) I want to iterate over possible matches with two bits set in a 16bit word. Seems like a silly problem with a currently overly-complex solution.
Iteration should return (decimal) 3,5,6,9,10,12,17...
What's the proper word for the problem? Bit-mask-looping?
Any clever function for this?
Current code - now updated:
(As it stands, i guess there's no easier way around this.)
<?php
function biterate($numBits=8, $setBits=2, $maxval=null) {
//init
if(is_null($maxval)) $maxval = (pow(2,$setBits)-1) * pow(2,$numBits - $setBits);
$err = 0;
header('content-type:text/plain');
echo '-- ' . $setBits . ' of ' . $numBits . " --\r\n";
$result = str_pad('', $numBits - $setBits, '0') . str_pad('', $setBits, '1');
do {
$err++;
if($err > 200) die('bad code');
//echo and calc next val.
echo $result . ' : ' . bindec($result) . "\r\n";
//count set bits and search for '01' to be replaced with '10'. From LSB.
$bitDivend = '';
$hit = false;
for($i=$numBits;$i>0;$i--) {
if(substr($result,$i-2,2) == '01') {
$hit = true;
//do the replacement and replace the lower part with bitDivend.
$result = substr($result, 0, $i-2) . '10';
$result .= str_pad('',$numBits - $i - strlen($bitDivend),'0');
$result .= $bitDivend;
//exit loop
$i = 0;
}
if($result[$i-1] == '1') $bitDivend .= '1';
}
} while($hit && bindec($result) <= $maxval);
}
biterate(8,2);
biterate(8,7);
biterate();
If you just want all the 16 bit ints with 2 bits set, the following code should do it:
<?php
for($i=1;$i<16;$i++)
{
for($j=0;$j<$i;$j++)
{
echo (1<<$i)|(1<<$j) , "\r\n";
}
}
?>
If you look at the bit patterns of the numbers you can see how it works:
11 3
101 5
110 6
1001 9
1010 10
1100 12
10001 17
10010 18
10100 20
11000 24
etc. You just move the most significant bit one place to the left (another power of 2) for each iteration of the outer loop, and inside the inner loop you iterate from the least significant bit (1) to 1 place to the right of the most significant bit.
If you wanted to generalise this to support an arbitrary number of bits and places, you could extend the above algorithm using recursion:
<?php
function biterate_recursive($numBits=8, $setBits=2, $initialValue=0, $maxval=null) {
for($i=$setBits-1;$i<$numBits;$i++)
{
if(!is_null($maxval) && ($initialValue|(1<<$i)) > $maxval)
break;
if($setBits==1)
echo $initialValue|(1<<$i) , "\r\n";
else
biterate_recursive($i, $setBits-1, $initialValue|(1<<$i), $maxval);
}
}
biterate_recursive(16, 2);
?>
You can also think of the problem as just choosing combinations i.e. C(16,2) choosing 2 numbers a,b from the set 0-15, and then calculating (1<<a)|(1<<b). However you have to be careful about your choice of combination algorithm if you want to get the numbers in order.
Im trying to get out an average value from a vote function.
<?php
$file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = str_split($textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum = $sum + intval($vote);
}
echo "Average: " . $sum;
?>
Simple by substitute (+) with a (/), and even tried a (%). But still getting error message.
Would appreciate alot if anyone could help me out and tell me what im doing wrong.
/thanks
Edit
Sidenote: Please read an explanation under "First answer given" further down below.
This version will take into account any blank lines in a file, if the content looks like:
1
2
3
// <- blank line
Sidenote: Please provide a sample of your text file. A comment has already been given to that effect.
PHP
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = array_filter(array_map("trim", file("textfile.txt")), "strlen");
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
First answer given
Using the following in a text file: (since no example of file content was given)
5
5
5
IMPORTANT NOTE: There should not be a carriage return after the last entry.
produced
Number of votes: 5
Average: 3
which is false, since there are 3 entries in the text file.
explode() should be used, and not str_split()
The following using the same text file produced:
Number of votes: 3
Average: 5
which is correct. In simple mathematics, averages are done by adding all numbers then dividing them by how many numbers there are.
In this case it's 3 numbers (all 5's) added equals 15, divided by 3 is 5.
Sidenote: The first line is not required $file = file("textfile2.txt");
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = explode("\n", $textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
Footnotes:
If the average comes out to 8.33333 and you would like it to be rounded off to 8, use:
echo "Average: " . floor($avg);
If the average comes out to 8.33333 and would like it to be as 9 you would use:
echo "Average: " . ceil($avg);
ceil() function
floor() function
You may be mixing in stuff that can't be divided, like text, etc. I don't know what your text file looks like. intval may be having a problem with arrays. You may try:
foreach ($textfill as $vote) {
if(is_int($vote) {
$sum += $vote;
}
}
echo "Average: " . $sum;
Lower school math says:
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
The average value is calculated by divide the sum with the number of votes. This line will print the average value:
echo "Average: " . $sum/count($textfill);
I have the following arrays:
$artist = array("the roots", "michael jackson", "billy idol", "more", "and more", "and_YET_MORE");
$count = array(5, 3, 9, 1, 1, 3);
I want to generate a tag cloud that will have artists with a higher number in $count enclosed in h6 tags and the lowest enclosed h1 tags.
You will want to add a logarithmic function to it too. (taken from tagadelic, my Drupal module to create tag clouds http://drupal.org/project/tagadelic):
db_query('SELECT COUNT(*) AS count, id, name FROM ... ORDER BY count DESC');
$steps = 6;
$tags = array();
$min = 1e9;
$max = -1e9;
while ($tag = db_fetch_object($result)) {
$tag->number_of_posts = $tag->count; #sets the amount of items a certain tag has attached to it
$tag->count = log($tag->count);
$min = min($min, $tag->count);
$max = max($max, $tag->count);
$tags[$tag->tid] = $tag;
}
// Note: we need to ensure the range is slightly too large to make sure even
// the largest element is rounded down.
$range = max(.01, $max - $min) * 1.0001;
foreach ($tags as $key => $value) {
$tags[$key]->weight = 1 + floor($steps * ($value->count - $min) / $range);
}
Then in your view or template:
foreach ($tags as $tag) {
$output .= "<h$tag->weight>$tag->name</h$tag->weight>"
}
Off the top of my head...
$artist = array("the roots","michael jackson","billy idol","more","and more","and_YET_MORE");
$count = array(5,3,9,1,1,3);
$highest = max($count);
for (int $x = 0; $x < count($artist); $x++)
{
$normalized = $count[$x] / $highest;
$heading = ceil($normalized * 6); // 6 heading types
echo "<h".$heading.">".$artist[$x]."</h".$heading.">";
}
Perhaps this is a little academic and off topic but hX tags are probably not the best choice for a tag cloud for reasons of document structure and all that sort of thing.
Maybe spans or an ol with appropriate class attributes (plus some CSS)?
Have used this snippet for a while, credit is prism-perfect.net. Doesn't use H tags though
<div id="tags">
<div class="title">Popular Searches</div>
<?php
// Snippet taken from [prism-perfect.net]
include "/path/to/public_html/search/settings/database.php";
include "/path/to/public_html/search/settings/conf.php";
$query = "SELECT query AS tag, COUNT(*) AS quantity
FROM sphider_query_log
WHERE results > 0
GROUP BY query
ORDER BY query ASC
LIMIT 10";
$result = mysql_query($query) or die(mysql_error());
while ($row = mysql_fetch_array($result)) {
$tags[$row['tag']] = $row['quantity'];
}
// change these font sizes if you will
$max_size = 30; // max font size in %
$min_size = 11; // min font size in %
// get the largest and smallest array values
$max_qty = max(array_values($tags));
$min_qty = min(array_values($tags));
// find the range of values
$spread = $max_qty - $min_qty;
if (0 == $spread) { // we don't want to divide by zero
$spread = 1;
}
// determine the font-size increment
// this is the increase per tag quantity (times used)
$step = ($max_size - $min_size)/($spread);
// loop through our tag array
foreach ($tags as $key => $value) {
// calculate CSS font-size
// find the $value in excess of $min_qty
// multiply by the font-size increment ($size)
// and add the $min_size set above
$size = $min_size + (($value - $min_qty) * $step);
// uncomment if you want sizes in whole %:
// $size = ceil($size);
// you'll need to put the link destination in place of the /search/search.php...
// (assuming your tag links to some sort of details page)
echo '<a href="/search/search.php?query='.$key.'&search=1" style="font-size: '.$size.'px"';
// perhaps adjust this title attribute for the things that are tagged
echo ' title="'.$value.' things tagged with '.$key.'"';
echo '>'.$key.'</a> ';
// notice the space at the end of the link
}
?>
</div>
#Ryan
That's correct but it actually makes the tags with the least number, larger. This code has been tested:
$artist = array("the roots","michael jackson","billy idol","more","and more","and_YET_MORE");
$count = array(5,3,9,1,1,3);
$highest = max($count);
for ($x = 0; $x < count($artist); $x++) {
$normalized = ($highest - $count[$x]+1) / $highest;
$heading = ceil($normalized * 6); // 6 heading types
echo "<h$heading>{$artist[$x]}</h$heading>";
}
This method is for SQL/PostgreSQL fanatics. It does the entire job in the database, and it prints text with "slugified" link. It uses Doctrine ORM just for the sql call, I'm not using objects.
Suppose we have 10 sizes:
public function getAllForTagCloud($fontSizes = 10)
{
$sql = sprintf("SELECT count(tag) as tagcount,tag,slug,
floor((count(*) * %d )/(select max(t) from
(select count(tag) as t from magazine_tag group by tag) t)::numeric(6,2))
as ranking
from magazine_tag mt group by tag,slug", $fontSizes);
$q = Doctrine_Manager::getInstance()->getCurrentConnection();
return $q->execute($sql);
}
then you print them with some CSS class, from .tagranking10 (the best) to .tagranking1 (the worst):
<?php foreach ($allTags as $tag): ?>
<span class="<?php echo 'tagrank'.$tag['ranking'] ?>">
<?php echo sprintf('<a rel="tag" href="/search/by/tag/%s">%s</a>',
$tag['slug'], $tag['tag']
); ?>
</span>
<?php endforeach; ?>
and this is the CSS:
/* put your size of choice */
.tagrank1{font-size: 0.3em;}
.tagrank2{font-size: 0.4em;}
.tagrank3{font-size: 0.5em;}
/* go on till tagrank10 */
This method displays all tags. If you have a lot of them, you probably don't want your tag cloud to become a tag storm. In that case you would append an HAVING TO clause to your SQL query:
-- minimum tag count is 8 --
HAVING count(tag) > 7
That's all
I know it's a very old post, still I'm posting my view as it may help someone in future.
Here is the tagcloud I used in my website:
http://www.vbausefulcodes.in/
<?php
$input= array("vba","macros","excel","outlook","powerpoint","access","database","interview questions","sendkeys","word","excel projects","visual basic projects","excel vba","macro","excel visual basic","tutorial","programming","learn macros","vba examples");
$rand_tags = array_rand($input, 5);
for ($x = 0; $x <= 4; $x++) {
$size = rand ( 1 , 4 );
echo "<font size='$size'>" . $input[$rand_tags[$x]] . " " . "</font>";
}
echo "<br>";
$rand_tags = array_rand($input, 7);
for ($x = 0; $x <= 6; $x++) {
$size = rand ( 1 , 4 );
echo "<font size='$size'>" . $input[$rand_tags[$x]] . " " . "</font>";
}
echo "<br>";
$rand_tags = array_rand($input, 5);
for ($x = 0; $x <= 4; $x++) {
$size = rand ( 1 , 4 );
echo "<font size='$size'>" . $input[$rand_tags[$x]] . " " . "</font>";
}
?>
As a helper in Rails:
def tag_cloud (strings, counts)
max = counts.max
strings.map { |a| "<span style='font-size:#{((counts[strings.index(a)] * 4.0)/max).ceil}em'>#{a}</span> " }
end
Call this from the view:
<%= tag_cloud($artists, $counts) %>
This outputs <span style='font-size:_em'> elements in an array that will be converted to a string in the view to ultimately render like so:
<span style='font-size:3em'>the roots</span>
<span style='font-size:2em'>michael jackson</span>
<span style='font-size:4em'>billy idol</span>
<span style='font-size:1em'>more</span>
<span style='font-size:1em'>and more</span>
<span style='font-size:2em'>and_YET_MORE</span>
It would be better to have a class attribute and reference the classes in a style sheet as mentioned by Brendan above. Much better than using h1-h6 semantically and there's less style baggage with a <span>.