Aggregating an import
Follow rejected rows, numeric precision and result order through the pipeline. Idiomatic vectorized or sequence APIs need not look like the DirectSharp loop. See readability under real business rules for policy branches, initialization and coordination.
A sales import needs approved totals by country. Each row supplies a country string, a decimal amount and an approval flag. Country keys are normalized by removing ASCII spaces at either end and converting ASCII letters to uppercase. Rows with an absent or empty normalized country, an absent row or a negative amount fail the import, even when unapproved. Valid unapproved rows contribute nothing.
For comparability, country inputs here are ASCII, monetary amounts are finite nonnegative decimals, and the result is unordered. No CSV parser or locale-dependent numeric conversion is hidden in one version. A parsing stage must enforce these input types before aggregation.
Row contract
The DirectSharp, C# and VB.NET implementations use an OrderRow class with readable Country (String), Amount (Decimal) and Approved (Boolean) properties. The Python version uses a matching dataclass:
from dataclasses import dataclass
from decimal import Decimal
@dataclass(frozen=True)
class OrderRow:
country: str
amount: Decimal
approved: bool
Freezing this input object prevents accidental mutation during aggregation. The .NET versions can use constructor-assigned, getter-only properties for the same public contract. The aggregation functions neither mutate rows nor depend on their construction syntax.
DirectSharp
The action belongs inside the import service. Its fully qualified types are ordinary .NET collection interfaces and implementations.
// Import policy: preserve validation, numeric meaning and output order.
action Aggregate(rows as Global.System.Collections.Generic.IEnumerable <of OrderRow>)
returns Global.System.Collections.Generic.Dictionary <of String, Decimal>
require rows is not nothing otherwise new Global.System.ArgumentNullException("rows")
let totals = new Global.System.Collections.Generic.Dictionary <of String, Decimal>(
Global.System.StringComparer.Ordinal)
for_each row in rows
require row is not nothing otherwise new Global.System.ArgumentException("A row is absent")
require row.Country is not nothing otherwise new Global.System.ArgumentException("Country is required")
require row.Amount >= 0 otherwise new Global.System.ArgumentException("Amount must be nonnegative")
let country = row.Country.Trim(" " as Char).ToUpperInvariant()
require country.Length > 0 otherwise new Global.System.ArgumentException("Country is required")
if row.Approved then
if not totals.ContainsKey(country) then
totals.Add(country, 0)
end if
totals[country] = totals[country] + row.Amount
end if
end for_each
return totals
end action
The ordinary loop processes one row at a time. Every row is validated before the approval decision. Dictionary access is strict; the explicit insertion establishes the key before reading its current total. There is no silent missing-key read or inferred default insertion.
C#
// Import policy: preserve validation, numeric meaning and output order.
using System;
using System.Collections.Generic;
public static Dictionary<string, decimal> Aggregate(IEnumerable<OrderRow> rows)
{
if (rows is null)
throw new ArgumentNullException(nameof(rows));
var totals = new Dictionary<string, decimal>(StringComparer.Ordinal);
foreach (OrderRow row in rows)
{
if (row is null)
throw new ArgumentException("A row is absent");
if (row.Country is null)
throw new ArgumentException("Country is required");
if (row.Amount < 0)
throw new ArgumentException("Amount must be nonnegative");
string country = row.Country.Trim(' ').ToUpperInvariant();
if (country.Length == 0)
throw new ArgumentException("Country is required");
if (row.Approved)
{
if (!totals.ContainsKey(country))
totals.Add(country, 0m);
totals[country] = totals[country] + row.Amount;
}
}
return totals;
}
VB.NET
' Import policy: preserve validation, numeric meaning and output order.
Option Strict On
Option Explicit On
Option Infer On
Imports System
Imports System.Collections.Generic
Public Function Aggregate(rows As IEnumerable(Of OrderRow)) As Dictionary(Of String, Decimal)
If rows Is Nothing Then Throw New ArgumentNullException(NameOf(rows))
Dim totals As New Dictionary(Of String, Decimal)(StringComparer.Ordinal)
For Each row As OrderRow In rows
If row Is Nothing Then Throw New ArgumentException("A row is absent")
If row.Country Is Nothing Then Throw New ArgumentException("Country is required")
If row.Amount < 0D Then Throw New ArgumentException("Amount must be nonnegative")
Dim country As String = row.Country.Trim(" "c).ToUpperInvariant()
If country.Length = 0 Then Throw New ArgumentException("Country is required")
If row.Approved Then
If Not totals.ContainsKey(country) Then
totals.Add(country, 0D)
End If
totals(country) = totals(country) + row.Amount
End If
Next
Return totals
End Function
VB.NET's parentheses select the dictionary indexer. DirectSharp and C# use brackets. These are access-syntax differences; every version explicitly handles a missing accumulated key before performing a strict read.
Python
# Import policy: preserve validation, numeric meaning and output order.
from collections.abc import Iterable
from decimal import Decimal
def aggregate(rows: Iterable[OrderRow]) -> dict[str, Decimal]:
if rows is None:
raise ValueError("rows is required")
totals: dict[str, Decimal] = {}
for row in rows:
if row is None:
raise ValueError("A row is absent")
if row.country is None:
raise ValueError("Country is required")
if row.amount < 0:
raise ValueError("Amount must be nonnegative")
country = row.country.strip(" ").upper()
if len(country) == 0:
raise ValueError("Country is required")
if row.approved:
if country not in totals:
totals[country] = Decimal("0")
totals[country] = totals[country] + row.amount
return totals
The Python loop follows the same order, including validation of ignored rows. It could use dict.get(country, Decimal("0")) to shorten the update. C# and VB.NET could similarly use TryGetValue, and DirectSharp can call that ordinary API. The explicit versions keep the missing-key policy equally visible.
A concrete import
| Country | Amount | Approved |
|---|---|---|
" gb " |
12.50 | Yes |
"GB" |
7.25 | Yes |
"de" |
9.00 | No |
"DE" |
2.00 | Yes |
The result is GB → 19.75 and DE → 2.00. Replacing the unapproved DE row's amount with -9.00 must fail rather than silently skip it. Empty input returns an empty dictionary. An exception from the source enumerator propagates; no partially built totals are returned.
All versions perform one pass and retain one total per distinct normalized key. Dictionary insertion order is not part of this contract. A caller needing a stable report order should sort explicitly before rendering.
Text policy is separate from identifier policy
Case-insensitive DirectSharp identifiers do not determine country normalization. The program explicitly calls ToUpperInvariant and uses an ordinal dictionary comparer. Python explicitly calls upper. Under the ASCII input contract these produce the same keys. Beyond ASCII, Unicode case mappings and runtime Unicode versions matter; an international identifier scheme needs a defined normalization policy.
C# and VB.NET expose the same .NET LINQ libraries. DirectSharp can also call them directly. Python offers generator expressions and comprehensions. A shorter query can be appropriate, but moving validation after filtering would change this import's error behavior. A comparison should preserve that order before judging its syntax.
Selecting a preview window
The strict/Try calls below select DirectSharp.Collections.SequenceExtensions explicitly or through its opt-in namespace; imported LINQ remains separate. Equally applicable extension candidates require qualification.
An import screen may show the first ten rows before processing the full file. This exposes a library-contract difference that syntax alone can hide. DirectSharp's own strict Take(10) requires ten items; ordinary LINQ Enumerable.Take(source, 10) returns the available prefix when fewer exist. Calling LINQ from DirectSharp preserves LINQ's behavior. The library symbol actually selected determines the contract.
For a source containing three rows:
| Operation | Outcome and when it is observed |
|---|---|
DirectSharp strict Take(10) |
Deferred traversal can yield the three rows, then throws ArgumentOutOfRangeException when it discovers the shortage |
DirectSharp tolerant TryTake(10) |
Collects the three rows at the call and returns a present selection |
.NET LINQ Enumerable.Take(source, 10) |
Returns a deferred sequence that yields the available three rows |
Python list(itertools.islice(source, 10)) |
Reads at the call and returns a list containing the available three rows |
On empty input, DirectSharp's TryTake(10) returns actual nothing; LINQ's sequence and Python's collected list are present empty results. Strict Take(0) is also present empty. Tolerant means the helper accepts an unavailable selection; it does not suppress source-reading failures. DirectSharp's two-position Take(start, end) has a zero-based inclusive end, so it must not be mechanically translated into Python's exclusive-stop slicing.
Choose the contract for the application. A preview usually wants available rows. A fixed-width import block may need a shortage to fail. A deferred strict sequence can already have delivered a prefix when that failure is discovered, and a consumer stopping early may never discover it. Validate or collect before committing external effects when partial processing would be unacceptable. See the sequence contracts and reference C# patterns for negative counts, tail selection, disposal and execution timing.