Browse files

Update implementation of Array.uniq and uniq! to use blocks correctly…

… (as per rubyspec)

Bugfix uniq! when dealing with recursive arrays (as per rubyspec)
  • Loading branch information...
1 parent a3d5012 commit 5a52d461eae90ab2a25d021279af37b19df6b6d8 Orion Edwards [GGL] committed Jan 4, 2012
Showing with 36 additions and 18 deletions.
  1. +33 −15 Languages/Ruby/Libraries/Extensions/IListOps.cs
  2. +3 −3 Languages/Ruby/Libraries/Initializers.Generated.cs
View
48 Languages/Ruby/Libraries/Extensions/IListOps.cs
@@ -633,18 +633,23 @@ public static class IListOps {
return result;
}
- private static void AddUniqueItems(IList/*!*/ list, IList/*!*/ result, Dictionary<object, bool> seen, ref bool nilSeen) {
+ private static void AddUniqueItems(BlockParam block, IList/*!*/ list, IList/*!*/ result, Dictionary<object, bool> seen, ref bool nilSeen) {
foreach (object item in list) {
- if (item == null) {
+ object key;
+ if (block == null || block.Yield(item, out key)) {
+ key = item;
+ }
+
+ if (key == null) {
if (!nilSeen) {
nilSeen = true;
- result.Add(null);
+ result.Add(item);
}
continue;
}
- if (!seen.ContainsKey(item)) {
- seen.Add(item, true);
+ if (!seen.ContainsKey(key)) {
+ seen.Add(key, true);
result.Add(item);
}
}
@@ -658,9 +663,9 @@ public static class IListOps {
var result = new RubyArray();
// Union merges the two arrays, removing duplicates
- AddUniqueItems(self, result, seen, ref nilSeen);
+ AddUniqueItems(null, self, result, seen, ref nilSeen);
- AddUniqueItems(other, result, seen, ref nilSeen);
+ AddUniqueItems(null, other, result, seen, ref nilSeen);
return result;
}
@@ -1881,39 +1886,52 @@ public static class IListOps {
}
[RubyMethod("uniq")]
- public static IList/*!*/ Unique(UnaryOpStorage/*!*/ allocateStorage, IList/*!*/ self) {
+ public static IList/*!*/ Unique(UnaryOpStorage/*!*/ allocateStorage, BlockParam block, IList/*!*/ self) {
IList result = CreateResultArray(allocateStorage, self);
var seen = new Dictionary<object, bool>(allocateStorage.Context.EqualityComparer);
bool nilSeen = false;
- AddUniqueItems(self, result, seen, ref nilSeen);
+ AddUniqueItems(block, self, result, seen, ref nilSeen);
return result;
}
[RubyMethod("uniq!")]
- public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, RubyArray/*!*/ self) {
+ public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, BlockParam block, RubyArray/*!*/ self) {
self.RequireNotFrozen();
- return UniqueSelf(hashStorage, eqlStorage, (IList)self);
+ return UniqueSelf(hashStorage, eqlStorage, block, (IList)self);
}
[RubyMethod("uniq!")]
- public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self) {
- var seen = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
+ public static IList UniqueSelf(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage, BlockParam block, IList/*!*/ self) {
+ IEqualityComparer<object> comparer = new EqualityComparer(hashStorage, eqlStorage);
+ var seen = new Dictionary<object, bool>(comparer);
bool nilSeen = false;
bool modified = false;
int i = 0;
while (i < self.Count) {
- object key = self[i];
+ object item = self[i];
+ object key;
+ if (block == null || block.Yield(item, out key)) {
+ key = item;
+ }
+
if (key != null && !seen.ContainsKey(key)) {
seen.Add(key, true);
i++;
} else if (key == null && !nilSeen) {
nilSeen = true;
i++;
} else {
- self.RemoveAt(i);
+ // removing objects modifies the array... which breaks the dictionary if "seen" is a self-referential array
+ bool isRecursiveArray = seen.Remove(self);
+
+ self.RemoveAt(i);
modified = true;
+
+ if (isRecursiveArray) {
+ seen.Add(self, true); // self.hash will change
+ }
}
}
View
6 Languages/Ruby/Libraries/Initializers.Generated.cs
@@ -8203,13 +8203,13 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
DefineLibraryMethod(module, "uniq", 0x51,
0x00000000U,
- new Func<IronRuby.Runtime.UnaryOpStorage, System.Collections.IList, System.Collections.IList>(IronRuby.Builtins.IListOps.Unique)
+ new Func<IronRuby.Runtime.UnaryOpStorage, IronRuby.Runtime.BlockParam, System.Collections.IList, System.Collections.IList>(IronRuby.Builtins.IListOps.Unique)
);
DefineLibraryMethod(module, "uniq!", 0x51,
0x00000000U, 0x00000000U,
- new Func<IronRuby.Runtime.UnaryOpStorage, IronRuby.Runtime.BinaryOpStorage, IronRuby.Builtins.RubyArray, System.Collections.IList>(IronRuby.Builtins.IListOps.UniqueSelf),
- new Func<IronRuby.Runtime.UnaryOpStorage, IronRuby.Runtime.BinaryOpStorage, System.Collections.IList, System.Collections.IList>(IronRuby.Builtins.IListOps.UniqueSelf)
+ new Func<IronRuby.Runtime.UnaryOpStorage, IronRuby.Runtime.BinaryOpStorage, IronRuby.Runtime.BlockParam, IronRuby.Builtins.RubyArray, System.Collections.IList>(IronRuby.Builtins.IListOps.UniqueSelf),
+ new Func<IronRuby.Runtime.UnaryOpStorage, IronRuby.Runtime.BinaryOpStorage, IronRuby.Runtime.BlockParam, System.Collections.IList, System.Collections.IList>(IronRuby.Builtins.IListOps.UniqueSelf)
);
DefineLibraryMethod(module, "unshift", 0x51,

0 comments on commit 5a52d46

Please sign in to comment.