morpheus.HClusterGroupBy = function(dataset, groupByFieldNames,
		distanceFunction, linkageMethod) {
	var model = dataset.getRowMetadata();
	var vectors = morpheus.MetadataUtil.getVectors(dataset.getRowMetadata(),
			groupByFieldNames);
	var idToIndices = morpheus.VectorUtil.createValuesToIndicesMap(vectors);
	var reorderedIndices = [];
	var offset = 0;
	var root = {
		id : -1,
		children : [],
		height : 0
	};
	var tree = {
		maxHeight : 0,
		rootNode : root,
		leafNodes : [],
		nLeafNodes : 0
	};
	idToIndices
			.forEach(function(rowIndices, id) {
				var originalIndicesForGroup = idToIndices.get(id);
				var subset = morpheus.DatasetUtil.slicedView(dataset,
						originalIndicesForGroup, null);
				var hcl;

				var distanceMatrix = morpheus.HCluster.computeDistanceMatrix(
						subset, distanceFunction);
				hcl = new morpheus.HCluster(distanceMatrix, linkageMethod);
				var reorderedGroupIndices = hcl.reorderedIndices;
				for (var i = 0, rows = subset.getRowCount(); i < rows; i++) {
					var originalIndex = originalIndicesForGroup[reorderedGroupIndices[i]];
					reorderedIndices.push(originalIndex);
				}

				morpheus.AbstractDendrogram.dfs(hcl.tree.rootNode, function(
						node) {
					node.index += offset;
					node.minIndex += offset;
					node.maxIndex += offset;
					node.id += offset;
					return true;
				});
				if (hcl.tree.leafNodes.length === 0) {
					tree.leafNodes = tree.leafNodes
							.concat([ hcl.tree.rootNode ]);
				} else {
					tree.leafNodes = tree.leafNodes.concat(hcl.tree.leafNodes);

				}

				root.children.push(hcl.tree.rootNode);
				if (!isNaN(hcl.tree.maxHeight)) {
					tree.maxHeight = Math.max(tree.maxHeight,
							hcl.tree.maxHeight);
				}
				offset += subset.getRowCount();
			});
	tree.nLeafNodes = tree.leafNodes.length;
	tree.rootNode.height = tree.maxHeight;
	this.tree = tree;
	this.reorderedIndices = reorderedIndices;
};