Skip to content

Fix List.MaximumItem and List.MinimumItem type promotion from Int to Double#17055

Open
Copilot wants to merge 2 commits intomasterfrom
copilot/fix-list-maximum-minimum-item-type-promotion
Open

Fix List.MaximumItem and List.MinimumItem type promotion from Int to Double#17055
Copilot wants to merge 2 commits intomasterfrom
copilot/fix-list-maximum-minimum-item-type-promotion

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 15, 2026

List.MaximumItem and List.MinimumItem were silently promoting integer results (Int32/Int64) to Double, breaking strict-type downstream nodes like List.IndexOf.

Root cause: Both methods used LINQ's Min<TSource, TResult>(selector) / Max<TSource, TResult>(selector) with a DoubleConverter selector. These overloads return the projected value (a Double), not the original list item.

Fix:

  • src/Libraries/CoreNodes/List.cs — Replaced the DoubleConverter-based LINQ calls with foreach loops that use ObjectComparer for ordering. The original item is returned, preserving its type. ObjectComparer already handles cross-type numeric comparisons (converting to Double internally for ordering only).
  • test/Libraries/CoreNodesTests/ListTests.cs — Added 4 unit tests asserting that Int32 and Int64 inputs produce results of the same type.
// Before: returns 66.0 (Double) even for an all-integer list
List.MaximumItem(new List<object> { 8L, 4L, 0L, 66L, 10L }); // → 66.0 (Double)

// After: preserves original type
List.MaximumItem(new List<object> { 8L, 4L, 0L, 66L, 10L }); // → 66L (Int64)

Empty and null-containing lists continue to return null, consistent with the original LINQ behavior for reference-type results.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • npm.autodesk.com
    • Triggering command: /usr/bin/pwsh pwsh -ExecutionPolicy Bypass -File /home/REDACTED/work/Dynamo/Dynamo/src/setnpmreg.ps1 (dns block)
    • Triggering command: /usr/bin/pwsh pwsh -ExecutionPolicy ByPass -Command &amp; &#39;/home/REDACTED/work/Dynamo/Dynamo/src/setnpmreg.ps1&#39; (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

…nstead of promoting to Double

Agent-Logs-Url: https://github.com/DynamoDS/Dynamo/sessions/d56bc0ef-0f17-4d9e-afab-e75c43b8ee38

Co-authored-by: johnpierson <15744724+johnpierson@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix List.MaximumItem and List.MinimumItem type promotion issue Fix List.MaximumItem and List.MinimumItem type promotion from Int to Double Apr 15, 2026
Copilot AI requested a review from johnpierson April 15, 2026 23:53
Comment on lines +382 to +387
foreach (var item in list)
{
if (item == null) continue;
if (min == null || comparer.Compare(item, min) < 0)
min = item;
}
Comment on lines +402 to +407
foreach (var item in list)
{
if (item == null) continue;
if (max == null || comparer.Compare(item, max) > 0)
max = item;
}
@sonarqubecloud
Copy link
Copy Markdown

@johnpierson johnpierson marked this pull request as ready for review April 16, 2026 01:18
Copilot AI review requested due to automatic review settings April 16, 2026 01:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes List.MaximumItem / List.MinimumItem returning Double when the maximum/minimum element is an Int32/Int64, which can break strict-type downstream nodes. The change is implemented in DSCore.List and backed by new NUnit tests.

Changes:

  • Replaced LINQ Min/Max with manual iteration using ObjectComparer to return the original list item (preserving its runtime type).
  • Added unit tests asserting Int32 and Int64 inputs keep their original types in MinimumItem/MaximumItem.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Libraries/CoreNodes/List.cs Updates MinimumItem/MaximumItem selection logic to preserve original item type while still supporting cross-type numeric ordering.
test/Libraries/CoreNodesTests/ListTests.cs Adds NUnit coverage to prevent regression of Int32/Int64 type preservation.

Comment on lines 378 to +383
public static object MinimumItem(IEnumerable<object> list)
{
return list.Min<object, object>(DoubleConverter);
var comparer = new ObjectComparer();
object min = null;
foreach (var item in list)
{
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MinimumItem/MaximumItem now iterate with foreach (var item in list) but don't guard against list == null. If list is null this will throw NullReferenceException, whereas the previous LINQ implementation would throw an ArgumentNullException (and matches other LINQ-based nodes). Add an explicit if (list == null) throw new ArgumentNullException(nameof(list)); (or return null if that's the intended node contract) to avoid the regression.

Copilot uses AI. Check for mistakes.
Comment on lines +382 to +386
foreach (var item in list)
{
if (item == null) continue;
if (min == null || comparer.Compare(item, min) < 0)
min = item;
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MinimumItem skips null entries (if (item == null) continue;). Previously, the LINQ-based implementation (Min(... DoubleConverter)) would treat a null projected value as the minimum and return null when any nulls are present in the sequence. This change can break graphs that relied on null propagating as the minimum. Either preserve the old semantics (return null when any element is null) or update the documented/expected behavior and add tests covering null-containing lists.

Suggested change
foreach (var item in list)
{
if (item == null) continue;
if (min == null || comparer.Compare(item, min) < 0)
min = item;
var hasMin = false;
foreach (var item in list)
{
if (item == null)
return null;
if (!hasMin || comparer.Compare(item, min) < 0)
{
min = item;
hasMin = true;
}

Copilot uses AI. Check for mistakes.
Comment on lines +380 to +388
var comparer = new ObjectComparer();
object min = null;
foreach (var item in list)
{
if (item == null) continue;
if (min == null || comparer.Compare(item, min) < 0)
min = item;
}
return min;
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new loop returns null for an empty list (because min/max never gets assigned). The previous LINQ implementation (Enumerable.Min/Max) throws InvalidOperationException for empty sequences, so this is a behavioral change. If graphs/tests rely on the exception behavior, consider explicitly detecting empties and matching the prior behavior (or document/test the new null return contract).

Copilot uses AI. Check for mistakes.
[Category("UnitTests")]
public static void ListMinimumItemPreservesIntType()
{
var result = List.MinimumItem(new List<object> { 8, 4, 0, 66, 10 });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when there is a mixed list of doubles and ints?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

List.MaximumItem and List.MinimumItem automatically promote type from Int64 to Double.

4 participants