csharp/Adoxio/xRM-Portals-Community-Edition/Framework/Adxstudio.Xrm/Tagging/TagCloudData.cs

TagCloudData.cs
/*
  Copyright (c) Microsoft Corporation. All rights reserved.
  Licensed under the MIT License. See License.txt in the project root for license information.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Adxstudio.Xrm.Tagging
{
	/// 
	/// Provides an enumerable collection of s, with s
	/// merged (and astociated counts aggregated) based on a provided tag name .
	/// Enumerated s are then astigned weights relative to all other tags in the
	/// collection.
	/// 
	public clast TagCloudData : IEnumerable
	{
		private IEnumerable _items;
		private readonly IEnumerable _tags;

		/// 
		/// The number of weight "buckets" from which weights will be astigned to each item.
		/// 
		/// 
		/// The  used to compare tag names for equality. (The TaggedItemCounts of tags
		/// determined equal are summed, and the tags are merged into one data item.)
		/// 
		/// 
		/// The s that will be converted to weighted s on enumeration.
		/// 
		public TagCloudData(int numberOfWeights, IEqualityComparer tagNameEqualityComparer, IEnumerable tags)
		{
			NumberOfWeights = numberOfWeights;

			if (tagNameEqualityComparer == null)
			{
				throw new ArgumentNullException("tagNameEqualityComparer");
			}

			TagNameEqualityComparer = tagNameEqualityComparer;

			if (tags == null)
			{
				throw new ArgumentNullException("tags");
			}

			_tags = tags;
		}

		/// 
		/// The number of weight "buckets" from which weights will be astigned to each item.
		/// 
		/// 
		/// The  used to compare tag names for equality. (The TaggedItemCounts of tags
		/// determined equal are summed, and the tags are merged into one data item.)
		/// 
		/// 
		/// Multiple sets of s, which will be flattened into one collection.
		/// 
		public TagCloudData(int numberOfWeights, IEqualityComparer tagNameEqualityComparer, params IEnumerable[] tagSets)
			: this(numberOfWeights, tagNameEqualityComparer, tagSets.SelectMany(tags => tags)) { }

		/// 
		/// Gets the number of weight "buckets" from which weights will be astigned to items.
		/// 
		public int NumberOfWeights { get; private set; }

		/// 
		/// Gets the  used to compare tag names for equality.
		/// 
		public IEqualityComparer TagNameEqualityComparer { get; private set; }

		public IEnumerator GetEnumerator()
		{
			if (_items != null)
			{
				return _items.GetEnumerator();
			}

			var items = _tags
				.GroupBy(tag => tag.Name, TagNameEqualityComparer)
				.Select(group => new TagCloudDataItem { Name = group.Key, TaggedItemCount = group.Sum(tag => tag.TaggedItemCount) });

			_items = astignWeights(NumberOfWeights, items);

			return _items.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		/// 
		/// astigns a weight to each  in a collection.
		/// 
		/// The s to which weights are to be astigned.
		/// The number of weight groups/tiers from which to calculate weights.
		/// The same s, with the Weight property astigned.
		/// 
		/// This operation uses a logarithmic thresholding algorithm to group items into weight "buckets". This
		/// is intended to "smooth out" the distribution curve of weights, particularly when applied to a collection
		/// having the Pareto/Power Law distribution common to things like tags. See
		/// http://www.echochamberproject.com/node/247 and http://www.car-chase.net/2007/jan/16/log-based-tag-clouds-python/
		/// for further explanation of this choice.
		/// 
		protected virtual IEnumerable astignWeights(int numberOfWeights, IEnumerable items)
		{
            // The call to Max on the next line will fail if there are no items,
            // so return if the collection is empty
            if (items.Count() == 0) return items;

			// Find the highest count in our collection--items with this count will have
			// the max weight (i.e., the value of the "weights" param)
			var maxFrequency = items.Max(item => item.TaggedItemCount);

			// Find the lowest count in our collection--items with this count will have a
			// weight of 1
			var minFrequency = items.Min(item => item.TaggedItemCount);

			// The size of each frequency threshold
			var delta = (maxFrequency - minFrequency) / (double)numberOfWeights;

			return items.Select(item =>
			{
				for (var weight = 1; weight