internal static List<GroupInfo> DoGrouping(List<OrderByPropertyEntry> orderMatrix, OrderByPropertyComparer comparer, bool noElement) { if (((orderMatrix == null) || (orderMatrix.Count == 0)) || (comparer == null)) { return null; } List<GroupInfo> groups = new List<GroupInfo>(); foreach (OrderByPropertyEntry entry in orderMatrix) { int num = FindInObjectGroups(groups, entry, comparer); if (num == -1) { tracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Create a new group: {0}", new object[] { entry.orderValues }), new object[0]); GroupInfo item = noElement ? new GroupInfoNoElement(entry) : new GroupInfo(entry); groups.Add(item); } else { tracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Add to group {0}: {1}", new object[] { num, entry.orderValues }), new object[0]); PSObject inputObject = entry.inputObject; groups[num].Add(inputObject); } } return groups; }
/// <summary> /// Moves unique entries to the front of the list. /// </summary> private int MoveUniqueEntriesToFront(List <OrderByPropertyEntry> sortedData, OrderByPropertyComparer comparer) { // If we have sorted data then we know we have at least one unique item int uniqueCount = sortedData.Count > 0 ? 1 : 0; // Move the first of each unique entry to the front of the list for (int uniqueItemIndex = 0, nextUniqueItemIndex = 1; uniqueItemIndex < sortedData.Count && uniqueCount != Top; uniqueItemIndex++, nextUniqueItemIndex++) { // Identify the index of the next unique item while (nextUniqueItemIndex < sortedData.Count && comparer.Compare(sortedData[uniqueItemIndex], sortedData[nextUniqueItemIndex]) == 0) { nextUniqueItemIndex++; } // If there are no more unique items, break if (nextUniqueItemIndex == sortedData.Count) { break; } // Move the next unique item forward and increment the unique item counter sortedData[uniqueItemIndex + 1] = sortedData[nextUniqueItemIndex]; uniqueCount++; } return(uniqueCount); }
private static OrderByPropertyComparer CreateComparer( List <OrderByPropertyEntry> orderMatrix, List <MshParameter> mshParameterList, bool ascending, CultureInfo cultureInfo, bool caseSensitive) { if (orderMatrix == null || orderMatrix.Count == 0) { return(null); } bool?[] ascendingOverrides = null; if (mshParameterList != null && mshParameterList.Count != 0) { ascendingOverrides = new bool?[mshParameterList.Count]; for (int k = 0; k < ascendingOverrides.Length; k++) { object ascendingVal = mshParameterList[k].GetEntry( SortObjectParameterDefinitionKeys.AscendingEntryKey); object descendingVal = mshParameterList[k].GetEntry( SortObjectParameterDefinitionKeys.DescendingEntryKey); bool isAscendingDefined = isOrderEntryKeyDefined(ascendingVal); bool isDescendingDefined = isOrderEntryKeyDefined(descendingVal); if (!isAscendingDefined && !isDescendingDefined) { // if neither ascending nor descending is defined ascendingOverrides[k] = null; } else if (isAscendingDefined && isDescendingDefined && (bool)ascendingVal == (bool)descendingVal) { // if both ascending and descending defined but their values conflict // they are ignored. ascendingOverrides[k] = null; } else if (isAscendingDefined) { ascendingOverrides[k] = (bool)ascendingVal; } else { ascendingOverrides[k] = !(bool)descendingVal; } } } OrderByPropertyComparer comparer = OrderByPropertyComparer.CreateComparer(orderMatrix, ascending, ascendingOverrides, cultureInfo, caseSensitive); return(comparer); }
/// <summary> /// Sort unsorted OrderByPropertyEntry data using a full sort. /// </summary> private int FullSort(List <OrderByPropertyEntry> dataToSort, OrderByPropertyComparer comparer) { // Track how many items in the list are sorted int sortedItemCount = dataToSort.Count; // Future: It may be worth comparing List.Sort with SortedSet when handling unique // records in case SortedSet is faster (SortedSet was not an option in earlier // versions of PowerShell). dataToSort.Sort(comparer); if (Unique) { // Move unique entries to the front of the list (this is significantly faster // than removing them) sortedItemCount = MoveUniqueEntriesToFront(dataToSort, comparer); } return(sortedItemCount); }
/// <summary> /// Process every input object to group them. /// </summary> protected override void ProcessRecord() { if (InputObject != null && InputObject != AutomationNull.Value) { OrderByPropertyEntry currentEntry = null; if (!_hasProcessedFirstInputObject) { if (Property == null) { Property = OrderByProperty.GetDefaultKeyPropertySet(InputObject); } _orderByProperty.ProcessExpressionParameter(this, Property); currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); bool[] ascending = new bool[currentEntry.orderValues.Count]; for (int index = 0; index < currentEntry.orderValues.Count; index++) { ascending[index] = true; } _orderByPropertyComparer = new OrderByPropertyComparer(ascending, _cultureInfo, CaseSensitive); _hasProcessedFirstInputObject = true; } else { currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); } DoGrouping(currentEntry, this.NoElement, _groups, _tupleToGroupInfoMappingDictionary, _orderByPropertyComparer); } }
/// <summary> /// Utility function called by Group-Object to create Groups. /// </summary> /// <param name="currentObjectEntry">Input object that needs to be grouped.</param> /// <param name="noElement">true if we are not accumulating objects</param> /// <param name="groups">List containing Groups.</param> /// <param name="groupInfoDictionary">Dictionary used to keep track of the groups with hash of the property values being the key.</param> /// <param name="orderByPropertyComparer">The Comparer to be used while comparing to check if new group has to be created.</param> internal static void DoGrouping(OrderByPropertyEntry currentObjectEntry, bool noElement, List<GroupInfo> groups, Dictionary<object, GroupInfo> groupInfoDictionary, OrderByPropertyComparer orderByPropertyComparer) { if (currentObjectEntry != null && currentObjectEntry.orderValues != null && currentObjectEntry.orderValues.Count > 0) { object currentTupleObject = PSTuple.ArrayToTuple(currentObjectEntry.orderValues.ToArray()); GroupInfo currentGroupInfo = null; if (groupInfoDictionary.TryGetValue(currentTupleObject, out currentGroupInfo)) { if (currentGroupInfo != null) { //add this inputObject to an existing group currentGroupInfo.Add(currentObjectEntry.inputObject); } } else { bool isCurrentItemGrouped = false; for (int groupsIndex = 0; groupsIndex < groups.Count; groupsIndex++) { // Check if the current input object can be converted to one of the already known types // by looking up in the type to GroupInfo mapping. if (orderByPropertyComparer.Compare(groups[groupsIndex].GroupValue, currentObjectEntry) == 0) { groups[groupsIndex].Add(currentObjectEntry.inputObject); isCurrentItemGrouped = true; break; } } if (!isCurrentItemGrouped) { // create a new group s_tracer.WriteLine("Create a new group: {0}", currentObjectEntry.orderValues); GroupInfo newObjGrp = noElement ? new GroupInfoNoElement(currentObjectEntry) : new GroupInfo(currentObjectEntry); groups.Add(newObjGrp); groupInfoDictionary.Add(currentTupleObject, newObjGrp); } } } }
private void InitComparer() { if (null != _comparer) return; List<PSObject> referenceObjectList = new List<PSObject>(ReferenceObject); _orderByProperty = new OrderByProperty( this, referenceObjectList, Property, true, _cultureInfo, CaseSensitive); Diagnostics.Assert(_orderByProperty.Comparer != null, "no comparer"); Diagnostics.Assert( _orderByProperty.OrderMatrix != null && _orderByProperty.OrderMatrix.Count == ReferenceObject.Length, "no OrderMatrix"); if (_orderByProperty.Comparer == null || _orderByProperty.OrderMatrix == null || _orderByProperty.OrderMatrix.Count == 0) { return; } _comparer = _orderByProperty.Comparer; _referenceEntries = _orderByProperty.OrderMatrix; }
internal OrderByProperty(PSCmdlet cmdlet, List<PSObject> inputObjects, object[] expr, bool ascending, CultureInfo cultureInfo, bool caseSensitive) { ProcessExpressionParameter(inputObjects, cmdlet, expr, out this._mshParameterList); this.orderMatrix = CreateOrderMatrix(cmdlet, inputObjects, this._mshParameterList); this.comparer = CreateComparer(this.orderMatrix, this._mshParameterList, ascending, cultureInfo, caseSensitive); }
internal IndexedOrderByPropertyComparer(OrderByPropertyComparer orderByPropertyComparer) { _orderByPropertyComparer = orderByPropertyComparer; }
private static int FindInObjectGroups(List<GroupInfo> groups, OrderByPropertyEntry target, OrderByPropertyComparer comparer) { for (int i = 0; i < groups.Count; i++) { if (comparer.Compare(groups[i].GroupValue, target) == 0) { return i; } } return -1; }
/// <summary> /// Sort unsorted OrderByPropertyEntry data using an indexed min-/max-heap sort. /// </summary> private int Heapify(List <OrderByPropertyEntry> dataToSort, OrderByPropertyComparer orderByPropertyComparer) { // Instantiate the Heapify comparer, which takes index into account for sort stability var comparer = new IndexedOrderByPropertyComparer(orderByPropertyComparer); // Identify how many items will be in the heap and the current number of items int heapCount = 0; int heapCapacity = Stable ? int.MaxValue : Top > 0 ? Top : Bottom; // Identify the comparator (the value all comparisons will be made against based on whether we're // doing a Top N or Bottom N sort) // Note: All comparison results in the loop below are performed related to the value of the // comparator. OrderByPropertyComparer.Compare will return -1 to indicate that the lhs is smaller // if an ascending sort is being executed, or -1 to indicate that the lhs is larger if a descending // sort is being executed. The comparator will be -1 if we're executing a Top N sort, or 1 if we're // executing a Bottom N sort. These two pairs of states allow us to perform the proper comparison // regardless of whether we're executing an ascending or descending Top N or Bottom N sort. This // allows us to build a min-heap or max-heap for each of these sorts with the exact same logic. // Min-heap: used for faster processing of a top N descending sort and a bottom N ascending sort // Max-heap: used for faster processing of a top N ascending sort and a bottom N descending sort int comparator = Top > 0 ? -1 : 1; // For unique sorts, use a sorted set to avoid adding unique items to the heap SortedSet <OrderByPropertyEntry> uniqueSet = Unique ? new SortedSet <OrderByPropertyEntry>(orderByPropertyComparer) : null; // Tracking the index is necessary so that unsortable items can be output at the end, in the order // in which they were received. for (int dataIndex = 0, discardedDuplicates = 0; dataIndex < dataToSort.Count - discardedDuplicates; dataIndex++) { // Min-heap: if the heap is full and the root item is larger than the entry, discard the entry // Max-heap: if the heap is full and the root item is smaller than the entry, discard the entry if (heapCount == heapCapacity && comparer.Compare(dataToSort[0], dataToSort[dataIndex]) == comparator) { continue; } // If we're doing a unique sort and the entry is not unique, discard the duplicate entry if (Unique && !uniqueSet.Add(dataToSort[dataIndex])) { discardedDuplicates++; if (dataIndex != dataToSort.Count - discardedDuplicates) { // When discarding duplicates, replace them with an item at the end of the list and // adjust our counter so that we check the item we just swapped in next dataToSort[dataIndex] = dataToSort[dataToSort.Count - discardedDuplicates]; dataIndex--; } continue; } // Add the current item to the heap and bubble it up into the correct position int childIndex = dataIndex; while (childIndex > 0) { int parentIndex = ((childIndex > (heapCapacity - 1) ? heapCapacity : childIndex) - 1) >> 1; // Min-heap: if the child item is larger than its parent, break // Max-heap: if the child item is smaller than its parent, break if (comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]) == comparator) { break; } var temp = dataToSort[parentIndex]; dataToSort[parentIndex] = dataToSort[childIndex]; dataToSort[childIndex] = temp; childIndex = parentIndex; } heapCount++; // If the heap size is too large, remove the root and rearrange the heap if (heapCount > heapCapacity) { // Move the last item to the root and reset the heap count (this effectively removes the last item) dataToSort[0] = dataToSort[dataIndex]; heapCount = heapCapacity; // Bubble the root item down into the correct position int parentIndex = 0; int parentItemCount = heapCapacity >> 1; while (parentIndex < parentItemCount) { // Min-heap: use the smaller of the two children in the comparison // Max-heap: use the larger of the two children in the comparison int leftChildIndex = (parentIndex << 1) + 1; int rightChildIndex = leftChildIndex + 1; childIndex = rightChildIndex == heapCapacity || comparer.Compare(dataToSort[leftChildIndex], dataToSort[rightChildIndex]) != comparator ? leftChildIndex : rightChildIndex; // Min-heap: if the smallest child is larger than or equal to its parent, break // Max-heap: if the largest child is smaller than or equal to its parent, break int childComparisonResult = comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]); if (childComparisonResult == 0 || childComparisonResult == comparator) { break; } var temp = dataToSort[childIndex]; dataToSort[childIndex] = dataToSort[parentIndex]; dataToSort[parentIndex] = temp; parentIndex = childIndex; } } } dataToSort.Sort(0, heapCount, comparer); return(heapCount); }
private void InitComparer() { if (this.comparer == null) { List<PSObject> inputObjects = new List<PSObject>(this.ReferenceObject); this.orderByProperty = new OrderByProperty(this, inputObjects, this.Property, true, base._cultureInfo, (bool) base.CaseSensitive); if (((this.orderByProperty.Comparer != null) && (this.orderByProperty.OrderMatrix != null)) && (this.orderByProperty.OrderMatrix.Count != 0)) { this.comparer = this.orderByProperty.Comparer; this.referenceEntries = this.orderByProperty.OrderMatrix; } } }