function _layoutArray(settings, layout, side) {
		var groups = {};

		// Combine into like groups (e.g. `top`, `top2`, etc)
		$.each(layout, function (pos, val) {
			if (val === null) {
				return;
			}

			var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' ');

			if (!groups[splitPos[0]]) {
				groups[splitPos[0]] = {};
			}

			var align = splitPos.length === 1 ?
				'full' :
				splitPos[1].toLowerCase();
			var group = groups[splitPos[0]];
			var groupRun = function (contents, innerVal) {
				// If it is an object, then there can be multiple features contained in it
				if ($.isPlainObject(innerVal)) {
					Object.keys(innerVal).map(function (key) {
						contents.push({
							feature: key,
							opts: innerVal[key]
						});
					});
				}
				else {
					contents.push(innerVal);
				}
			}

			// Transform to an object with a contents property
			if (!group[align] || !group[align].contents) {
				group[align] = { contents: [] };
			}

			// Allow for an array or just a single object
			if (Array.isArray(val)) {
				for (var i = 0; i < val.length; i++) {
					groupRun(group[align].contents, val[i]);
				}
			}
			else {
				groupRun(group[align].contents, val);
			}

			// And make contents an array
			if (!Array.isArray(group[align].contents)) {
				group[align].contents = [group[align].contents];
			}
		});

		var filtered = Object.keys(groups)
			.map(function (pos) {
				// Filter to only the side we need
				if (pos.indexOf(side) !== 0) {
					return null;
				}

				return {
					name: pos,
					val: groups[pos]
				};
			})
			.filter(function (item) {
				return item !== null;
			});

		// Order by item identifier
		filtered.sort(function (a, b) {
			var order1 = a.name.replace(/[^0-9]/g, '') * 1;
			var order2 = b.name.replace(/[^0-9]/g, '') * 1;

			return order2 - order1;
		});

		if (side === 'bottom') {
			filtered.reverse();
		}

		// Split into rows
		var rows = [];
		for (var i = 0, ien = filtered.length; i < ien; i++) {
			if (filtered[i].val.full) {
				rows.push({ full: filtered[i].val.full });
				_layoutResolve(settings, rows[rows.length - 1]);

				delete filtered[i].val.full;
			}

			if (Object.keys(filtered[i].val).length) {
				rows.push(filtered[i].val);
				_layoutResolve(settings, rows[rows.length - 1]);
			}
		}

		return rows;
	}
Convert a `layout` object given by a user to the object structure needed
for the renderer. This is done twice, once for above and once for below
the table. Ordering must also be considered.

@param {*} settings DataTables settings object
@param {*} layout Layout object to convert
@param {string} side `top` or `bottom`
@returns Converted array structure - one item for each row.