What's the best way to generate a tag cloud from an array using h1 through h6 for sizing?

PhpArraysTag Cloud

Php Problem Overview


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.

Php Solutions


Solution 1 - Php

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>"
}

Solution 2 - Php

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.">";
}

Solution 3 - Php

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)?

Solution 4 - Php

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>

Solution 5 - Php

@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>";
}

Solution 6 - Php

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

Solution 7 - Php

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>.

Solution 8 - Php

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>";
}
?>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionw-llView Question on Stackoverflow
Solution 1 - PhpberkesView Answer on Stackoverflow
Solution 2 - PhpRyan FoxView Answer on Stackoverflow
Solution 3 - PhpBrendanView Answer on Stackoverflow
Solution 4 - PhpessexboyracerView Answer on Stackoverflow
Solution 5 - PhpKevinView Answer on Stackoverflow
Solution 6 - PhpdanieliView Answer on Stackoverflow
Solution 7 - PhpSwardsView Answer on Stackoverflow
Solution 8 - PhpDurgaprasadView Answer on Stackoverflow