This is an automated email from the ASF dual-hosted git repository.
curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new a715ea06b7 GH-38692: [C#] Implement ICollection<T?> on scalar arrays
(#41539)
a715ea06b7 is described below
commit a715ea06b71ec206a987d7921264778e9954404b
Author: Gavin Murrison <[email protected]>
AuthorDate: Mon May 13 16:38:14 2024 +0100
GH-38692: [C#] Implement ICollection<T?> on scalar arrays (#41539)
### What changes are included in this PR?
This PR makes the following array types support ICollection<T?> :
- PrimitiveArray
- BooleanArray
- Date32Array
- Date64Array
- Time32Array
- Time64Array
- BinaryArray
- TimestampArray
- StringArray
### Are these changes tested?
Yes
### Are there any user-facing changes?
No
Closes #38692
* GitHub Issue: #38692
Authored-by: voidstar69 <[email protected]>
Signed-off-by: Curt Hagenlocher <[email protected]>
---
csharp/src/Apache.Arrow/Arrays/BinaryArray.cs | 27 +++-
csharp/src/Apache.Arrow/Arrays/BooleanArray.cs | 29 ++++-
csharp/src/Apache.Arrow/Arrays/Date32Array.cs | 63 ++++++++-
csharp/src/Apache.Arrow/Arrays/Date64Array.cs | 63 ++++++++-
csharp/src/Apache.Arrow/Arrays/IntervalArray.cs | 2 +-
csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs | 37 +++++-
.../Apache.Arrow/Arrays/PrimitiveArrayBuilder.cs | 2 +-
csharp/src/Apache.Arrow/Arrays/StringArray.cs | 27 +++-
csharp/src/Apache.Arrow/Arrays/Time32Array.cs | 27 +++-
csharp/src/Apache.Arrow/Arrays/Time64Array.cs | 27 +++-
csharp/src/Apache.Arrow/Arrays/TimestampArray.cs | 27 +++-
csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs | 2 +-
.../test/Apache.Arrow.IntegrationTest/JsonFile.cs | 6 +-
csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs | 145 ++++++++++++++++++++-
csharp/test/Apache.Arrow.Tests/Date32ArrayTests.cs | 2 +-
.../Extensions/DateTimeOffsetExtensions.cs | 2 -
csharp/test/Apache.Arrow.Tests/UnionArrayTests.cs | 2 +-
17 files changed, 450 insertions(+), 40 deletions(-)
diff --git a/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs
b/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs
index 1bd4035d5b..0c84fa2be2 100644
--- a/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/BinaryArray.cs
@@ -22,7 +22,7 @@ using System.Collections;
namespace Apache.Arrow
{
- public class BinaryArray : Array, IReadOnlyList<byte[]>
+ public class BinaryArray : Array, IReadOnlyList<byte[]>,
ICollection<byte[]>
{
public class Builder : BuilderBase<BinaryArray, Builder>
{
@@ -380,5 +380,30 @@ namespace Apache.Arrow
}
IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable<byte[]>)this).GetEnumerator();
+
+ int ICollection<byte[]>.Count => Length;
+ bool ICollection<byte[]>.IsReadOnly => true;
+ void ICollection<byte[]>.Add(byte[]? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<byte[]>.Remove(byte[]? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<byte[]>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<byte[]>.Contains(byte[] item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetBytes(index).SequenceEqual(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<byte[]>.CopyTo(byte[][] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetBytes(srcIndex).ToArray();
+ }
+ }
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/BooleanArray.cs
b/csharp/src/Apache.Arrow/Arrays/BooleanArray.cs
index e9c5f8979e..19d4d0b7ed 100644
--- a/csharp/src/Apache.Arrow/Arrays/BooleanArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/BooleanArray.cs
@@ -21,7 +21,7 @@ using System.Collections.Generic;
namespace Apache.Arrow
{
- public class BooleanArray: Array, IReadOnlyList<bool?>
+ public class BooleanArray: Array, IReadOnlyList<bool?>, ICollection<bool?>
{
public class Builder : IArrowArrayBuilder<bool, BooleanArray, Builder>
{
@@ -188,7 +188,7 @@ namespace Apache.Arrow
public bool? GetValue(int index)
{
return IsNull(index)
- ? (bool?)null
+ ? null
: BitUtility.GetBit(ValueBuffer.Span, index + Offset);
}
@@ -205,5 +205,30 @@ namespace Apache.Arrow
}
IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable<bool?>)this).GetEnumerator();
+
+ int ICollection<bool?>.Count => Length;
+ bool ICollection<bool?>.IsReadOnly => true;
+ void ICollection<bool?>.Add(bool? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<bool?>.Remove(bool? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<bool?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<bool?>.Contains(bool? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetValue(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<bool?>.CopyTo(bool?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetValue(srcIndex);
+ }
+ }
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/Date32Array.cs
b/csharp/src/Apache.Arrow/Arrays/Date32Array.cs
index 6ab4986f57..55864e89e2 100644
--- a/csharp/src/Apache.Arrow/Arrays/Date32Array.cs
+++ b/csharp/src/Apache.Arrow/Arrays/Date32Array.cs
@@ -23,9 +23,9 @@ namespace Apache.Arrow
/// The <see cref="Date32Array"/> class holds an array of dates in the
<c>Date32</c> format, where each date is
/// stored as the number of days since the dawn of (UNIX) time.
/// </summary>
- public class Date32Array : PrimitiveArray<int>, IReadOnlyList<DateTime?>
+ public class Date32Array : PrimitiveArray<int>, IReadOnlyList<DateTime?>,
ICollection<DateTime?>
#if NET6_0_OR_GREATER
- , IReadOnlyList<DateOnly?>
+ , IReadOnlyList<DateOnly?>, ICollection<DateOnly?>
#endif
{
private static readonly DateTime _epochDate = new DateTime(1970, 1, 1,
0, 0, 0, DateTimeKind.Unspecified);
@@ -40,10 +40,9 @@ namespace Apache.Arrow
{
private class DateBuilder : PrimitiveArrayBuilder<int,
Date32Array, DateBuilder>
{
- protected override Date32Array Build(
- ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer,
- int length, int nullCount, int offset) =>
- new Date32Array(valueBuffer, nullBitmapBuffer, length,
nullCount, offset);
+ protected override Date32Array Build(ArrowBuffer valueBuffer,
ArrowBuffer nullBitmapBuffer, int length,
+ int nullCount, int offset) =>
+ new(valueBuffer, nullBitmapBuffer, length, nullCount,
offset);
}
/// <summary>
@@ -149,6 +148,31 @@ namespace Apache.Arrow
yield return GetDateOnly(index);
};
}
+
+ int ICollection<DateOnly?>.Count => Length;
+ bool ICollection<DateOnly?>.IsReadOnly => true;
+ void ICollection<DateOnly?>.Add(DateOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<DateOnly?>.Remove(DateOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<DateOnly?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<DateOnly?>.Contains(DateOnly? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetDateOnly(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<DateOnly?>.CopyTo(DateOnly?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetDateOnly(srcIndex);
+ }
+ }
#endif
int IReadOnlyCollection<DateTime?>.Count => Length;
@@ -160,7 +184,32 @@ namespace Apache.Arrow
for (int index = 0; index < Length; index++)
{
yield return GetDateTime(index);
- };
+ }
+ }
+
+ int ICollection<DateTime?>.Count => Length;
+ bool ICollection<DateTime?>.IsReadOnly => true;
+ void ICollection<DateTime?>.Add(DateTime? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<DateTime?>.Remove(DateTime? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<DateTime?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<DateTime?>.Contains(DateTime? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetDateTime(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<DateTime?>.CopyTo(DateTime?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetDateTime(srcIndex);
+ }
}
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/Date64Array.cs
b/csharp/src/Apache.Arrow/Arrays/Date64Array.cs
index 43e698e10b..77538ce59f 100644
--- a/csharp/src/Apache.Arrow/Arrays/Date64Array.cs
+++ b/csharp/src/Apache.Arrow/Arrays/Date64Array.cs
@@ -24,9 +24,9 @@ namespace Apache.Arrow
/// stored as the number of milliseconds since the dawn of (UNIX) time,
excluding leap seconds, in multiples of
/// 86400000.
/// </summary>
- public class Date64Array : PrimitiveArray<long>, IReadOnlyList<DateTime?>
+ public class Date64Array : PrimitiveArray<long>, IReadOnlyList<DateTime?>,
ICollection<DateTime?>
#if NET6_0_OR_GREATER
- , IReadOnlyList<DateOnly?>
+ , IReadOnlyList<DateOnly?>, ICollection<DateOnly?>
#endif
{
private const long MillisecondsPerDay = 86400000;
@@ -45,10 +45,9 @@ namespace Apache.Arrow
{
private class DateBuilder : PrimitiveArrayBuilder<long,
Date64Array, DateBuilder>
{
- protected override Date64Array Build(
- ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer,
- int length, int nullCount, int offset) =>
- new Date64Array(valueBuffer, nullBitmapBuffer, length,
nullCount, offset);
+ protected override Date64Array Build(ArrowBuffer valueBuffer,
ArrowBuffer nullBitmapBuffer, int length,
+ int nullCount, int offset) =>
+ new(valueBuffer, nullBitmapBuffer, length, nullCount,
offset);
}
/// <summary>
@@ -151,6 +150,31 @@ namespace Apache.Arrow
yield return GetDateOnly(index);
};
}
+
+ int ICollection<DateOnly?>.Count => Length;
+ bool ICollection<DateOnly?>.IsReadOnly => true;
+ void ICollection<DateOnly?>.Add(DateOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<DateOnly?>.Remove(DateOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<DateOnly?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<DateOnly?>.Contains(DateOnly? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetDateOnly(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<DateOnly?>.CopyTo(DateOnly?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetDateOnly(srcIndex);
+ }
+ }
#endif
int IReadOnlyCollection<DateTime?>.Count => Length;
@@ -162,7 +186,32 @@ namespace Apache.Arrow
for (int index = 0; index < Length; index++)
{
yield return GetDateTime(index);
- };
+ }
+ }
+
+ int ICollection<DateTime?>.Count => Length;
+ bool ICollection<DateTime?>.IsReadOnly => true;
+ void ICollection<DateTime?>.Add(DateTime? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<DateTime?>.Remove(DateTime? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<DateTime?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<DateTime?>.Contains(DateTime? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetDateTime(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<DateTime?>.CopyTo(DateTime?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetDateTime(srcIndex);
+ }
}
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/IntervalArray.cs
b/csharp/src/Apache.Arrow/Arrays/IntervalArray.cs
index de4fc42b4c..3949af877b 100644
--- a/csharp/src/Apache.Arrow/Arrays/IntervalArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/IntervalArray.cs
@@ -31,7 +31,7 @@ namespace Apache.Arrow
}
public abstract class IntervalArray<T> : PrimitiveArray<T>
- where T : struct
+ where T : struct, IEquatable<T>
{
protected IntervalArray(ArrayData data)
: base(data)
diff --git a/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs
b/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs
index 0456c5cc65..05d659b527 100644
--- a/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs
@@ -20,8 +20,8 @@ using System.Runtime.CompilerServices;
namespace Apache.Arrow
{
- public abstract class PrimitiveArray<T> : Array, IReadOnlyList<T?>
- where T : struct
+ public abstract class PrimitiveArray<T> : Array, IReadOnlyList<T?>,
ICollection<T?>
+ where T : struct, IEquatable<T>
{
protected PrimitiveArray(ArrayData data)
: base(data)
@@ -40,7 +40,7 @@ namespace Apache.Arrow
{
throw new ArgumentOutOfRangeException(nameof(index));
}
- return IsValid(index) ? Values[index] : (T?)null;
+ return IsValid(index) ? Values[index] : null;
}
public IList<T?> ToList(bool includeNulls = false)
@@ -86,5 +86,36 @@ namespace Apache.Arrow
yield return IsValid(index) ? Values[index] : null;
}
}
+
+ int ICollection<T?>.Count => Length;
+ bool ICollection<T?>.IsReadOnly => true;
+ void ICollection<T?>.Add(T? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<T?>.Remove(T? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<T?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<T?>.Contains(T? item)
+ {
+ if (item == null)
+ {
+ return NullCount > 0;
+ }
+
+ ReadOnlySpan<T> values = Values;
+ while (values.Length > 0)
+ {
+ int index = Values.IndexOf(item.Value);
+ if (index < 0 || IsValid(index)) { return index >= 0; }
+ values = values.Slice(index + 1);
+ }
+ return false;
+ }
+
+ void ICollection<T?>.CopyTo(T?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetValue(srcIndex);
+ }
+ }
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/PrimitiveArrayBuilder.cs
b/csharp/src/Apache.Arrow/Arrays/PrimitiveArrayBuilder.cs
index 67fe46633c..ae02173fb0 100644
--- a/csharp/src/Apache.Arrow/Arrays/PrimitiveArrayBuilder.cs
+++ b/csharp/src/Apache.Arrow/Arrays/PrimitiveArrayBuilder.cs
@@ -20,7 +20,7 @@ using System.Linq;
namespace Apache.Arrow
{
- public abstract class PrimitiveArrayBuilder<TFrom, TTo, TArray, TBuilder>
: IArrowArrayBuilder<TArray, TBuilder>
+ public abstract class PrimitiveArrayBuilder<TFrom, TTo, TArray, TBuilder>
: IArrowArrayBuilder<TFrom, TArray, TBuilder>
where TTo : struct
where TArray : IArrowArray
where TBuilder : class, IArrowArrayBuilder<TArray>
diff --git a/csharp/src/Apache.Arrow/Arrays/StringArray.cs
b/csharp/src/Apache.Arrow/Arrays/StringArray.cs
index a3ec596adc..ab44805d8d 100644
--- a/csharp/src/Apache.Arrow/Arrays/StringArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/StringArray.cs
@@ -22,7 +22,7 @@ using Apache.Arrow.Types;
namespace Apache.Arrow
{
- public class StringArray: BinaryArray, IReadOnlyList<string>
+ public class StringArray: BinaryArray, IReadOnlyList<string>,
ICollection<string>
{
public static readonly Encoding DefaultEncoding = Encoding.UTF8;
@@ -164,5 +164,30 @@ namespace Apache.Arrow
}
IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable<string>)this).GetEnumerator();
+
+ int ICollection<string>.Count => Length;
+ bool ICollection<string>.IsReadOnly => true;
+ void ICollection<string>.Add(string item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<string>.Remove(string item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<string>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<string>.Contains(string item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetString(index) == item)
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<string>.CopyTo(string[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetString(srcIndex);
+ }
+ }
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/Time32Array.cs
b/csharp/src/Apache.Arrow/Arrays/Time32Array.cs
index e9c2d7a4d9..63c0898935 100644
--- a/csharp/src/Apache.Arrow/Arrays/Time32Array.cs
+++ b/csharp/src/Apache.Arrow/Arrays/Time32Array.cs
@@ -26,7 +26,7 @@ namespace Apache.Arrow
/// </summary>
public class Time32Array : PrimitiveArray<int>
#if NET6_0_OR_GREATER
- , IReadOnlyList<TimeOnly?>
+ , IReadOnlyList<TimeOnly?>, ICollection<TimeOnly?>
#endif
{
/// <summary>
@@ -171,6 +171,31 @@ namespace Apache.Arrow
yield return GetTime(index);
};
}
+
+ int ICollection<TimeOnly?>.Count => Length;
+ bool ICollection<TimeOnly?>.IsReadOnly => true;
+ void ICollection<TimeOnly?>.Add(TimeOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<TimeOnly?>.Remove(TimeOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<TimeOnly?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<TimeOnly?>.Contains(TimeOnly? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetTime(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<TimeOnly?>.CopyTo(TimeOnly?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetTime(srcIndex);
+ }
+ }
#endif
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/Time64Array.cs
b/csharp/src/Apache.Arrow/Arrays/Time64Array.cs
index fc18dfb8bf..5518462952 100644
--- a/csharp/src/Apache.Arrow/Arrays/Time64Array.cs
+++ b/csharp/src/Apache.Arrow/Arrays/Time64Array.cs
@@ -26,7 +26,7 @@ namespace Apache.Arrow
/// </summary>
public class Time64Array : PrimitiveArray<long>
#if NET6_0_OR_GREATER
- , IReadOnlyList<TimeOnly?>
+ , IReadOnlyList<TimeOnly?>, ICollection<TimeOnly?>
#endif
{
/// <summary>
@@ -162,6 +162,31 @@ namespace Apache.Arrow
yield return GetTime(index);
};
}
+
+ int ICollection<TimeOnly?>.Count => Length;
+ bool ICollection<TimeOnly?>.IsReadOnly => true;
+ void ICollection<TimeOnly?>.Add(TimeOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ bool ICollection<TimeOnly?>.Remove(TimeOnly? item) => throw new
NotSupportedException("Collection is read-only.");
+ void ICollection<TimeOnly?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<TimeOnly?>.Contains(TimeOnly? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetTime(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<TimeOnly?>.CopyTo(TimeOnly?[] array, int arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetTime(srcIndex);
+ }
+ }
#endif
}
}
diff --git a/csharp/src/Apache.Arrow/Arrays/TimestampArray.cs
b/csharp/src/Apache.Arrow/Arrays/TimestampArray.cs
index ccb656854a..b838605847 100644
--- a/csharp/src/Apache.Arrow/Arrays/TimestampArray.cs
+++ b/csharp/src/Apache.Arrow/Arrays/TimestampArray.cs
@@ -21,7 +21,7 @@ using System.IO;
namespace Apache.Arrow
{
- public class TimestampArray : PrimitiveArray<long>,
IReadOnlyList<DateTimeOffset?>
+ public class TimestampArray : PrimitiveArray<long>,
IReadOnlyList<DateTimeOffset?>, ICollection<DateTimeOffset?>
{
private static readonly DateTimeOffset s_epoch = new
DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero);
@@ -157,5 +157,30 @@ namespace Apache.Arrow
yield return GetTimestamp(index);
};
}
+
+ int ICollection<DateTimeOffset?>.Count => Length;
+ bool ICollection<DateTimeOffset?>.IsReadOnly => true;
+ void ICollection<DateTimeOffset?>.Add(DateTimeOffset? item) => throw
new NotSupportedException("Collection is read-only.");
+ bool ICollection<DateTimeOffset?>.Remove(DateTimeOffset? item) =>
throw new NotSupportedException("Collection is read-only.");
+ void ICollection<DateTimeOffset?>.Clear() => throw new
NotSupportedException("Collection is read-only.");
+
+ bool ICollection<DateTimeOffset?>.Contains(DateTimeOffset? item)
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ if (GetTimestamp(index).Equals(item))
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<DateTimeOffset?>.CopyTo(DateTimeOffset?[] array, int
arrayIndex)
+ {
+ for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length;
srcIndex++, destIndex++)
+ {
+ array[destIndex] = GetTimestamp(srcIndex);
+ }
+ }
}
}
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
index b11479c0d4..c66569afeb 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
@@ -148,7 +148,7 @@ namespace Apache.Arrow.Ipc
public void Visit(MonthDayNanosecondIntervalArray array) =>
VisitPrimitiveArray(array);
private void VisitPrimitiveArray<T>(PrimitiveArray<T> array)
- where T : struct
+ where T : struct, IEquatable<T>
{
_buffers.Add(CreateBitmapBuffer(array.NullBitmapBuffer,
array.Offset, array.Length));
_buffers.Add(CreateSlicedBuffer<T>(array.ValueBuffer,
array.Offset, array.Length));
diff --git a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
index 31a5676f01..7232f74b8b 100644
--- a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
+++ b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
@@ -908,8 +908,8 @@ namespace Apache.Arrow.IntegrationTest
};
private void GenerateArray<T, TArray>(Func<ArrowBuffer,
ArrowBuffer, int, int, int, TArray> createArray)
+ where T : struct, IEquatable<T>
where TArray : PrimitiveArray<T>
- where T : struct
{
ArrowBuffer validityBuffer = GetValidityBuffer(out int
nullCount);
@@ -929,8 +929,8 @@ namespace Apache.Arrow.IntegrationTest
}
private void GenerateLongArray<T, TArray>(Func<ArrowBuffer,
ArrowBuffer, int, int, int, TArray> createArray, Func<string, T> parse)
+ where T : struct, IEquatable<T>
where TArray : PrimitiveArray<T>
- where T : struct
{
ArrowBuffer validityBuffer = GetValidityBuffer(out int
nullCount);
@@ -950,8 +950,8 @@ namespace Apache.Arrow.IntegrationTest
}
private void GenerateArray<T, TArray>(Func<ArrowBuffer,
ArrowBuffer, int, int, int, TArray> createArray, Func<JsonElement, T> construct)
+ where T : struct, IEquatable<T>
where TArray : PrimitiveArray<T>
- where T : struct
{
ArrowBuffer validityBuffer = GetValidityBuffer(out int
nullCount);
diff --git a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs
b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs
index 682ebec323..d3032b8d4a 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs
@@ -101,9 +101,9 @@ namespace Apache.Arrow.Tests
{
var array = new Int64Array.Builder().Append(1).Append(2).Build();
- foreach(long? foo in (IEnumerable<long?>)array)
+ foreach(long? foo in array)
{
- Assert.InRange(foo.Value, 1, 2);
+ Assert.InRange(foo!.Value, 1, 2);
}
foreach (object foo in (IEnumerable)array)
@@ -115,12 +115,145 @@ namespace Apache.Arrow.Tests
[Fact]
public void ArrayAsReadOnlyList()
{
- Int64Array array = new
Int64Array.Builder().Append(1).Append(2).Build();
- var readOnlyList = (IReadOnlyList<long?>)array;
+ TestArrayAsReadOnlyList<long, Int64Array, Int64Array.Builder>([1,
2]);
+ TestArrayAsReadOnlyList<byte, UInt8Array, UInt8Array.Builder>([1,
2]);
+ TestArrayAsReadOnlyList<bool, BooleanArray,
BooleanArray.Builder>([true, false]);
+ TestArrayAsReadOnlyList<DateTime, Date32Array,
Date32Array.Builder>([DateTime.MinValue.Date, DateTime.MaxValue.Date]);
+ TestArrayAsReadOnlyList<DateTime, Date64Array,
Date64Array.Builder>([DateTime.MinValue.Date, DateTime.MaxValue.Date]);
+ TestArrayAsReadOnlyList<DateTimeOffset, TimestampArray,
TimestampArray.Builder>([DateTimeOffset.MinValue,
DateTimeOffset.MinValue.AddYears(100)]);
+
+#if NET5_0_OR_GREATER
+ TestArrayAsReadOnlyList<DateOnly, Date32Array,
Date32Array.Builder>([DateOnly.MinValue, DateOnly.MaxValue]);
+ TestArrayAsReadOnlyList<DateOnly, Date64Array,
Date64Array.Builder>([DateOnly.MinValue, DateOnly.MaxValue]);
+ TestArrayAsReadOnlyList<TimeOnly, Time32Array,
Time32Array.Builder>([TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23)]);
+ TestArrayAsReadOnlyList<TimeOnly, Time64Array,
Time64Array.Builder>([TimeOnly.MinValue, TimeOnly.MaxValue]);
+ TestArrayAsReadOnlyList<Half, HalfFloatArray,
HalfFloatArray.Builder>([(Half)1.1, (Half)2.2f]);
+#endif
+ }
+
+ // Parameter 'values' must contain two distinct values
+ private static void TestArrayAsReadOnlyList<T, TArray,
TArrayBuilder>(IReadOnlyList<T> values)
+ where T : struct
+ where TArray : IArrowArray
+ where TArrayBuilder : IArrowArrayBuilder<T, TArray,
TArrayBuilder>, new()
+ {
+ Assert.Equal(2, values.Count);
+ TArray array = new
TArrayBuilder().Append(values[0]).AppendNull().Append(values[1]).Build(default);
+ Assert.NotNull(array);
+ var readOnlyList = (IReadOnlyList<T?>)array;
Assert.Equal(array.Length, readOnlyList.Count);
- Assert.Equal(readOnlyList[0], 1);
- Assert.Equal(readOnlyList[1], 2);
+ Assert.Equal(3, readOnlyList.Count);
+ Assert.Equal(values[0], readOnlyList[0]);
+ Assert.Null(readOnlyList[1]);
+ Assert.Equal(values[1], readOnlyList[2]);
+ }
+
+ [Fact]
+ public void ArrayAsCollection()
+ {
+ TestPrimitiveArrayAsCollection<long, Int64Array,
Int64Array.Builder>([1, 2, 3, 4]);
+ TestPrimitiveArrayAsCollection<byte, UInt8Array,
UInt8Array.Builder>([1, 2, 3, 4]);
+ TestPrimitiveArrayAsCollection<bool, BooleanArray,
BooleanArray.Builder>([true, true, true, false]);
+ TestPrimitiveArrayAsCollection<DateTime, Date32Array,
Date32Array.Builder>([DateTime.MinValue.Date, DateTime.MaxValue.Date,
DateTime.Today, DateTime.Today]);
+ TestPrimitiveArrayAsCollection<DateTime, Date64Array,
Date64Array.Builder>([DateTime.MinValue.Date, DateTime.MaxValue.Date,
DateTime.Today, DateTime.Today]);
+ TestPrimitiveArrayAsCollection<DateTimeOffset, TimestampArray,
TimestampArray.Builder>([DateTimeOffset.MinValue,
DateTimeOffset.MinValue.AddYears(100), DateTimeOffset.Now,
DateTimeOffset.UtcNow]);
+
+#if NET5_0_OR_GREATER
+ TestPrimitiveArrayAsCollection<DateOnly, Date32Array,
Date32Array.Builder>([DateOnly.MinValue, DateOnly.MaxValue,
DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2)]);
+ TestPrimitiveArrayAsCollection<DateOnly, Date64Array,
Date64Array.Builder>([DateOnly.MinValue, DateOnly.MaxValue,
DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2)]);
+ TestPrimitiveArrayAsCollection<TimeOnly, Time32Array,
Time32Array.Builder>([TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23),
TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2)]);
+ TestPrimitiveArrayAsCollection<TimeOnly, Time64Array,
Time64Array.Builder>([TimeOnly.MinValue, TimeOnly.MaxValue,
TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2)]);
+ TestPrimitiveArrayAsCollection<Half, HalfFloatArray,
HalfFloatArray.Builder>([(Half)1.1, (Half)2.2f, (Half)3.3f, (Half)4.4f]);
+#endif
+
+ byte[][] byteArrs = [new byte[1], [], [255], new byte[2]];
+ TestObjectArrayAsCollection(new
BinaryArray.Builder().Append(byteArrs[0].AsEnumerable()).AppendNull().Append(byteArrs[1].AsEnumerable()).Append(byteArrs[0].AsEnumerable()).Build(),
System.Array.Empty<byte>(), byteArrs);
+
+ string[] strings = ["abc", "abd", "acd", "adc"];
+ TestObjectArrayAsCollection(new
StringArray.Builder().Append(strings[0]).AppendNull().Append(strings[1]).Append(strings[0]).Build(),
null, strings);
+ }
+
+ // Parameter 'values' must contain four values. The last value must be
distinct from the rest.
+ private static void TestPrimitiveArrayAsCollection<T, TArray,
TArrayBuilder>(IReadOnlyList<T> values)
+ where T : struct
+ where TArray : IArrowArray, ICollection<T?>
+ where TArrayBuilder : IArrowArrayBuilder<T, TArray,
TArrayBuilder>, new()
+ {
+ Assert.Equal(4, values.Count);
+ TArray array = new
TArrayBuilder().Append(values[0]).AppendNull().Append(values[1]).Append(values[0]).Build(default);
+ Assert.NotNull(array);
+ var collection = (ICollection<T?>)array;
+
+ Assert.Equal(array.Length, collection.Count);
+ Assert.Equal(4, collection.Count);
+ Assert.True(collection.IsReadOnly);
+
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(() => collection.Add(values[3])).Message);
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(() =>
collection.Remove(values[3])).Message);
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(collection.Clear).Message);
+
+ Assert.True(collection.Contains(values[0]));
+ Assert.True(collection.Contains(values[1]));
+ Assert.True(collection.Contains(default));
+ Assert.False(collection.Contains(values[3]));
+
+ T sentinel = values[2];
+ T?[] destArr = { sentinel, sentinel, sentinel, sentinel, sentinel,
sentinel };
+ collection.CopyTo(destArr, 1);
+ Assert.Equal(sentinel, destArr[0]);
+ Assert.Equal(values[0], destArr[1]);
+ Assert.Null(destArr[2]);
+ Assert.Equal(values[1], destArr[3]);
+ Assert.Equal(values[0], destArr[4]);
+ Assert.Equal(sentinel, destArr[0]);
+ }
+
+ // Parameter 'values' must contain four values. The last value must be
distinct from the rest.
+ private static void TestObjectArrayAsCollection<T, TArray>(TArray
array, T nullValue, IReadOnlyList<T> values)
+ where T : class
+ where TArray : IArrowArray, ICollection<T?>
+ {
+ Assert.NotNull(array);
+ Assert.Equal(4, values.Count);
+ var collection = (ICollection<T?>)array;
+
+ Assert.Equal(array.Length, collection.Count);
+ Assert.Equal(4, collection.Count);
+ Assert.True(collection.IsReadOnly);
+
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(() => collection.Add(values[3])).Message);
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(() =>
collection.Remove(values[3])).Message);
+ Assert.Equal("Collection is read-only.",
Assert.Throws<NotSupportedException>(collection.Clear).Message);
+
+ Assert.True(collection.Contains(values[0]));
+ Assert.True(collection.Contains(values[1]));
+ Assert.True(collection.Contains(default));
+ Assert.False(collection.Contains(values[3]));
+
+ T sentinel = values[2];
+ T?[] destArr = { sentinel, sentinel, sentinel, sentinel, sentinel,
sentinel };
+ collection.CopyTo(destArr, 1);
+ Assert.Equal(sentinel, destArr[0]);
+ Assert.Equal(values[0], destArr[1]);
+ Assert.Equal(nullValue, destArr[2]);
+ Assert.Equal(values[1], destArr[3]);
+ Assert.Equal(values[0], destArr[4]);
+ Assert.Equal(sentinel, destArr[0]);
+ }
+
+ [Fact]
+ public void ContainsDoesNotMatchDefaultValueInArrayWithNullValue()
+ {
+ Int64Array array = new
Int64Array.Builder().Append(1).Append(2).AppendNull().Build();
+ Assert.NotNull(array);
+ var collection = (ICollection<long?>)array;
+
+ Assert.True(collection.Contains(1));
+ Assert.True(collection.Contains(2));
+ Assert.True(collection.Contains(default));
+ // A null value is stored as a null bit in the null bitmap, and a
default value in the value buffer. Check that we do not match the default value.
+ Assert.False(collection.Contains(0));
}
[Fact]
diff --git a/csharp/test/Apache.Arrow.Tests/Date32ArrayTests.cs
b/csharp/test/Apache.Arrow.Tests/Date32ArrayTests.cs
index 2a674b942c..6e4742cad0 100644
--- a/csharp/test/Apache.Arrow.Tests/Date32ArrayTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/Date32ArrayTests.cs
@@ -131,7 +131,7 @@ namespace Apache.Arrow.Tests
public class AppendDateOnly
{
[Theory]
- [MemberData(nameof(GetDateOnlyData), MemberType =
typeof(Date64ArrayTests))]
+ [MemberData(nameof(GetDateOnlyData), MemberType =
typeof(Date32ArrayTests))]
public void AppendDateGivesSameDate(DateOnly date)
{
// Arrange
diff --git
a/csharp/test/Apache.Arrow.Tests/Extensions/DateTimeOffsetExtensions.cs
b/csharp/test/Apache.Arrow.Tests/Extensions/DateTimeOffsetExtensions.cs
index 4375c39cdf..01809735d1 100644
--- a/csharp/test/Apache.Arrow.Tests/Extensions/DateTimeOffsetExtensions.cs
+++ b/csharp/test/Apache.Arrow.Tests/Extensions/DateTimeOffsetExtensions.cs
@@ -14,8 +14,6 @@
// limitations under the License.
using System;
-using System.Collections.Generic;
-using System.Text;
namespace Apache.Arrow.Tests
{
diff --git a/csharp/test/Apache.Arrow.Tests/UnionArrayTests.cs
b/csharp/test/Apache.Arrow.Tests/UnionArrayTests.cs
index 712a87a252..c603ef63a4 100644
--- a/csharp/test/Apache.Arrow.Tests/UnionArrayTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/UnionArrayTests.cs
@@ -110,7 +110,7 @@ public class UnionArrayTests
}
private static void CompareFieldValue<T, TArray>(byte typeId, UnionArray
originalArray, int originalIndex, UnionArray slicedArray, int sliceIndex)
- where T: struct
+ where T : struct, IEquatable<T>
where TArray : PrimitiveArray<T>
{
if (originalArray is DenseUnionArray denseOriginalArray)