diff --git a/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs b/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs index be7d0236d9970..05d659b5270ad 100644 --- a/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs +++ b/csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs @@ -100,7 +100,14 @@ IEnumerator IEnumerable.GetEnumerator() return NullCount > 0; } - return Values.IndexOf(item.Value) >= 0; + ReadOnlySpan 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.CopyTo(T?[] array, int arrayIndex) diff --git a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs index 912c649a582da..d3032b8d4ac40 100644 --- a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs +++ b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs @@ -115,19 +115,19 @@ public void EnumerateArray() [Fact] public void ArrayAsReadOnlyList() { - TestArrayAsReadOnlyList(new long[] { 1, 2 }); - TestArrayAsReadOnlyList(new byte[] { 1, 2 }); - TestArrayAsReadOnlyList(new[] { true, false }); - TestArrayAsReadOnlyList(new[] { DateTime.MinValue.Date, DateTime.MaxValue.Date }); - TestArrayAsReadOnlyList(new[] { DateTime.MinValue.Date, DateTime.MaxValue.Date }); - TestArrayAsReadOnlyList(new[] { DateTimeOffset.MinValue, DateTimeOffset.MinValue.AddYears(100) }); + TestArrayAsReadOnlyList([1, 2]); + TestArrayAsReadOnlyList([1, 2]); + TestArrayAsReadOnlyList([true, false]); + TestArrayAsReadOnlyList([DateTime.MinValue.Date, DateTime.MaxValue.Date]); + TestArrayAsReadOnlyList([DateTime.MinValue.Date, DateTime.MaxValue.Date]); + TestArrayAsReadOnlyList([DateTimeOffset.MinValue, DateTimeOffset.MinValue.AddYears(100)]); #if NET5_0_OR_GREATER - TestArrayAsReadOnlyList(new[] { DateOnly.MinValue, DateOnly.MaxValue }); - TestArrayAsReadOnlyList(new[] { DateOnly.MinValue, DateOnly.MaxValue}); - TestArrayAsReadOnlyList(new[] { TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23) }); - TestArrayAsReadOnlyList(new[] { TimeOnly.MinValue, TimeOnly.MaxValue }); - TestArrayAsReadOnlyList(new[] { (Half)1.1, (Half)2.2f }); + TestArrayAsReadOnlyList([DateOnly.MinValue, DateOnly.MaxValue]); + TestArrayAsReadOnlyList([DateOnly.MinValue, DateOnly.MaxValue]); + TestArrayAsReadOnlyList([TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23)]); + TestArrayAsReadOnlyList([TimeOnly.MinValue, TimeOnly.MaxValue]); + TestArrayAsReadOnlyList([(Half)1.1, (Half)2.2f]); #endif } @@ -152,25 +152,25 @@ private static void TestArrayAsReadOnlyList(IReadOnlyL [Fact] public void ArrayAsCollection() { - TestPrimitiveArrayAsCollection(new long[] { 1, 2, 3, 4 }); - TestPrimitiveArrayAsCollection(new byte[] { 1, 2, 3, 4 }); - TestPrimitiveArrayAsCollection(new[] { true, true, true, false }); - TestPrimitiveArrayAsCollection(new[] { DateTime.MinValue.Date, DateTime.MaxValue.Date, DateTime.Today, DateTime.Today }); - TestPrimitiveArrayAsCollection(new[] { DateTime.MinValue.Date, DateTime.MaxValue.Date, DateTime.Today, DateTime.Today }); - TestPrimitiveArrayAsCollection(new[] { DateTimeOffset.MinValue, DateTimeOffset.MinValue.AddYears(100), DateTimeOffset.Now, DateTimeOffset.UtcNow }); + TestPrimitiveArrayAsCollection([1, 2, 3, 4]); + TestPrimitiveArrayAsCollection([1, 2, 3, 4]); + TestPrimitiveArrayAsCollection([true, true, true, false]); + TestPrimitiveArrayAsCollection([DateTime.MinValue.Date, DateTime.MaxValue.Date, DateTime.Today, DateTime.Today]); + TestPrimitiveArrayAsCollection([DateTime.MinValue.Date, DateTime.MaxValue.Date, DateTime.Today, DateTime.Today]); + TestPrimitiveArrayAsCollection([DateTimeOffset.MinValue, DateTimeOffset.MinValue.AddYears(100), DateTimeOffset.Now, DateTimeOffset.UtcNow]); #if NET5_0_OR_GREATER - TestPrimitiveArrayAsCollection(new[] { DateOnly.MinValue, DateOnly.MaxValue, DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2) }); - TestPrimitiveArrayAsCollection(new[] { DateOnly.MinValue, DateOnly.MaxValue, DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2) }); - TestPrimitiveArrayAsCollection(new[] { TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23), TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2) }); - TestPrimitiveArrayAsCollection(new[] { TimeOnly.MinValue, TimeOnly.MaxValue, TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2) }); - TestPrimitiveArrayAsCollection(new[] { (Half)1.1, (Half)2.2f, (Half)3.3f, (Half)4.4f }); + TestPrimitiveArrayAsCollection([DateOnly.MinValue, DateOnly.MaxValue, DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2)]); + TestPrimitiveArrayAsCollection([DateOnly.MinValue, DateOnly.MaxValue, DateOnly.FromDayNumber(1), DateOnly.FromDayNumber(2)]); + TestPrimitiveArrayAsCollection([TimeOnly.MinValue, TimeOnly.MinValue.AddHours(23), TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2)]); + TestPrimitiveArrayAsCollection([TimeOnly.MinValue, TimeOnly.MaxValue, TimeOnly.MinValue.AddHours(1), TimeOnly.MinValue.AddHours(2)]); + TestPrimitiveArrayAsCollection([(Half)1.1, (Half)2.2f, (Half)3.3f, (Half)4.4f]); #endif - byte[][] byteArrs = { new byte[1], System.Array.Empty(), new byte[] { 255 }, new byte[2] }; + 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(), byteArrs); - string[] strings = { "abc", "abd", "acd", "adc" }; + string[] strings = ["abc", "abd", "acd", "adc"]; TestObjectArrayAsCollection(new StringArray.Builder().Append(strings[0]).AppendNull().Append(strings[1]).Append(strings[0]).Build(), null, strings); } @@ -242,6 +242,20 @@ private static void TestObjectArrayAsCollection(TArray array, T nullV 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)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] public void RecursiveArraySlice() {