Browse Source

* Adds removeAll: removeAllFoundIn: and removeAllSuchThat: to the collection API
* Adds one test for each of those methods.
* Upgrades dependency on amber-dev to 0.3.1

Sebastian Sastre 9 years ago
parent
commit
de0b3aa970
7 changed files with 408 additions and 1 deletions
  1. 8 0
      API-CHANGES.txt
  2. 10 0
      CHANGELOG
  3. 1 1
      package.json
  4. 109 0
      src/Kernel-Collections.js
  5. 27 0
      src/Kernel-Collections.st
  6. 204 0
      src/Kernel-Tests.js
  7. 49 0
      src/Kernel-Tests.st

+ 8 - 0
API-CHANGES.txt

@@ -1,3 +1,11 @@
+0.14.4:
+
++ Collection >>
+  + removeAll:
+  + removeAllFoundIn:
+  + removeAllSuchThat:
+
+
 0.14.3:
 
 * #heliosClass is now #classTag

+ 10 - 0
CHANGELOG

@@ -1,3 +1,13 @@
+21 January 2015 - Release 0.14.4
+===================================
+
+Highlights:
+
+* Adds removeAll:  removeAllFoundIn: and removeAllSuchThat: to the collection API
+* Adds one test for each of those methods.
+* Upgrades dependency on amber-dev to 0.3.1
+
+
 20 January 2015 - Release 0.14.3
 ===================================
 

+ 1 - 1
package.json

@@ -29,7 +29,7 @@
     "test": "grunt test && cd external && cd amber-cli && npm test && cd .. && cd .."
   },
   "devDependencies": {
-    "amber-dev": "^0.3.0",
+    "amber-dev": "^0.3.1",
     "grunt": "^0.4.0",
     "grunt-contrib-clean": "^0.6.0",
     "grunt-contrib-jshint": "^0.10.0",

+ 109 - 0
src/Kernel-Collections.js

@@ -1541,6 +1541,115 @@ messageSends: ["subclassResponsibility"]
 }),
 $globals.Collection);
 
+$core.addMethod(
+$core.method({
+selector: "removeAll:",
+protocol: 'adding/removing',
+fn: function (aCollection){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+$1=$recv(aCollection).__eq_eq(self);
+if($core.assert($1)){
+$2=self._removeAll();
+return $2;
+};
+$recv(aCollection)._do_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._remove_(each);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+return aCollection;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"removeAll:",{aCollection:aCollection},$globals.Collection)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection"],
+source: "removeAll: aCollection \x0a\x09\x22Remove each element of aCollection from the receiver. If successful for \x0a\x09each, answer aCollection. \x0a\x09Raises an error when an element is not found.\x22\x0a\x0a\x09aCollection == self ifTrue: [ ^self removeAll ].\x0a\x09aCollection do: [ :each | self remove: each ].\x0a\x09^ aCollection",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:", "==", "removeAll", "do:", "remove:"]
+}),
+$globals.Collection);
+
+$core.addMethod(
+$core.method({
+selector: "removeAllFoundIn:",
+protocol: 'adding/removing',
+fn: function (aCollection){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv(aCollection)._do_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._remove_ifAbsent_(each,(function(){
+return nil;
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return aCollection;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"removeAllFoundIn:",{aCollection:aCollection},$globals.Collection)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection"],
+source: "removeAllFoundIn: aCollection \x0a    \x22Remove from the receiver each element of aCollection that is also found in the receiver. \x0a\x09No error is raised if an element isn't found.\x0a\x09Answer aCollection.\x22\x0a\x0a    aCollection do: [ :each | self remove: each ifAbsent: [ nil ] ].\x0a    ^ aCollection",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["do:", "remove:ifAbsent:"]
+}),
+$globals.Collection);
+
+$core.addMethod(
+$core.method({
+selector: "removeAllSuchThat:",
+protocol: 'adding/removing',
+fn: function (aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$recv(self._copy())._do_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=$recv(aBlock)._value_(each);
+if($core.assert($1)){
+return self._remove_(each);
+};
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"removeAllSuchThat:",{aBlock:aBlock},$globals.Collection)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "removeAllSuchThat: aBlock \x0a\x09\x22Evaluate aBlock for each element and remove all the elements from\x0a\x09the receiver for which aBlock evaluates to true.  Use a copy to enumerate \x0a\x09collections whose order changes when an element is removed (i.e. Sets).\x22\x0a\x0a\x09self copy do: [ :each | (aBlock value: each) ifTrue: [ self remove: each ] ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["do:", "copy", "ifTrue:", "value:", "remove:"]
+}),
+$globals.Collection);
+
 $core.addMethod(
 $core.method({
 selector: "select:",

+ 27 - 0
src/Kernel-Collections.st

@@ -192,6 +192,33 @@ remove: anObject ifAbsent: aBlock
 
 removeAll
 	self subclassResponsibility
+!
+
+removeAll: aCollection 
+	"Remove each element of aCollection from the receiver. If successful for 
+	each, answer aCollection. 
+	Raises an error when an element is not found."
+
+	aCollection == self ifTrue: [ ^self removeAll ].
+	aCollection do: [ :each | self remove: each ].
+	^ aCollection
+!
+
+removeAllFoundIn: aCollection 
+    "Remove from the receiver each element of aCollection that is also found in the receiver. 
+	No error is raised if an element isn't found.
+	Answer aCollection."
+
+    aCollection do: [ :each | self remove: each ifAbsent: [ nil ] ].
+    ^ aCollection
+!
+
+removeAllSuchThat: aBlock 
+	"Evaluate aBlock for each element and remove all the elements from
+	the receiver for which aBlock evaluates to true.  Use a copy to enumerate 
+	collections whose order changes when an element is removed (i.e. Sets)."
+
+	self copy do: [ :each | (aBlock value: each) ifTrue: [ self remove: each ] ]
 ! !
 
 !Collection methodsFor: 'converting'!

+ 204 - 0
src/Kernel-Tests.js

@@ -3685,6 +3685,37 @@ messageSends: ["assert:equals:", "removeAll", "collection", "yourself", "new", "
 }),
 $globals.CollectionTest);
 
+$core.addMethod(
+$core.method({
+selector: "testRemoveSome",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var original,part;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+original=[(1), (2), (3), (4), (5), (6), (7), (8), (9)];
+part=[(2), (4), (6)];
+$1=self._collection();
+$recv($1)._removeAll();
+$2=$recv($1)._yourself();
+self._assert_equals_($2,$recv(self._collectionClass())._new());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRemoveSome",{original:original,part:part},$globals.CollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRemoveSome\x0a\x0a\x09| original part |\x0a\x09\x0a\x09original := #( 1 2 3 4 5 6 7 8 9 ).\x0a\x09part := #( 2 4 6 ).\x0a\x09\x0a\x09\x0a\x09self assert: (self collection removeAll; yourself) equals: self collectionClass new",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["assert:equals:", "removeAll", "collection", "yourself", "new", "collectionClass"]
+}),
+$globals.CollectionTest);
+
 $core.addMethod(
 $core.method({
 selector: "testSelect",
@@ -6697,6 +6728,179 @@ messageSends: ["assert:equals:", "last:", "collection", "collectionLastTwo", "ne
 }),
 $globals.SequenceableCollectionTest);
 
+$core.addMethod(
+$core.method({
+selector: "testRemoveAllFound",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var original,part;
+function $Error(){return $globals.Error||(typeof Error=="undefined"?nil:Error)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+original=[(1), (2), (3), (4), (5), (6), (7), (8), (9)];
+part=[(2), (4), (6)];
+$recv(original)._removeAllFoundIn_(part);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["removeAllFoundIn:"]=1;
+//>>excludeEnd("ctx");
+self._shouldnt_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._assert_($recv(part)._allSatisfy_((function(e){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(original)._includes_(e))._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({e:e},$ctx2,2)});
+//>>excludeEnd("ctx");
+})));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),$Error());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["shouldnt:raise:"]=1;
+//>>excludeEnd("ctx");
+self._shouldnt_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(original)._removeAllFoundIn_([(11), (12)]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)});
+//>>excludeEnd("ctx");
+}),$Error());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRemoveAllFound",{original:original,part:part},$globals.SequenceableCollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRemoveAllFound\x0a\x0a\x09| original part |\x0a\x09\x0a\x09original := #( 1 2 3 4 5 6 7 8 9 ).\x0a\x09part := #( 2 4 6 ).\x0a\x09\x0a\x09original removeAllFoundIn: part.\x0a\x0a\x09self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.\x0a\x09self shouldnt: [ original removeAllFoundIn: #( 11 12 ) ] raise: Error",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["removeAllFoundIn:", "shouldnt:raise:", "assert:", "allSatisfy:", "not", "includes:"]
+}),
+$globals.SequenceableCollectionTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRemoveAllSuch",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var original,part;
+function $Error(){return $globals.Error||(typeof Error=="undefined"?nil:Error)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+original=[(1), (2), (3), (4), (5), (6), (7), (8), (9)];
+part=[(2), (4), (6)];
+$recv(original)._removeAllSuchThat_((function(e){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(part)._includes_(e);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["includes:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+self._shouldnt_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._assert_($recv(part)._allSatisfy_((function(e){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(original)._includes_(e))._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({e:e},$ctx2,3)});
+//>>excludeEnd("ctx");
+})));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+//>>excludeEnd("ctx");
+}),$Error());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRemoveAllSuch",{original:original,part:part},$globals.SequenceableCollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRemoveAllSuch\x0a\x0a\x09| original part |\x0a\x09\x0a\x09original := #( 1 2 3 4 5 6 7 8 9 ).\x0a\x09part := #( 2 4 6 ).\x0a\x09\x0a\x09original removeAllSuchThat: [ :e | part includes: e ].\x0a\x0a\x09self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["removeAllSuchThat:", "includes:", "shouldnt:raise:", "assert:", "allSatisfy:", "not"]
+}),
+$globals.SequenceableCollectionTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRemoveAllUsingSome",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var original,part;
+function $Error(){return $globals.Error||(typeof Error=="undefined"?nil:Error)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+original=[(1), (2), (3), (4), (5), (6), (7), (8), (9)];
+part=[(2), (4), (6)];
+$recv(original)._removeAll_(part);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["removeAll:"]=1;
+//>>excludeEnd("ctx");
+self._shouldnt_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._assert_($recv(part)._allSatisfy_((function(e){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(original)._includes_(e))._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({e:e},$ctx2,2)});
+//>>excludeEnd("ctx");
+})));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),$Error());
+self._should_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(original)._removeAll_([(11), (12)]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)});
+//>>excludeEnd("ctx");
+}),$Error());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRemoveAllUsingSome",{original:original,part:part},$globals.SequenceableCollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRemoveAllUsingSome\x0a\x0a\x09| original part |\x0a\x09\x0a\x09original := #( 1 2 3 4 5 6 7 8 9 ).\x0a\x09part := #( 2 4 6 ).\x0a\x09\x0a\x09original removeAll: part.\x0a\x0a\x09self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.\x0a\x09self should: [ original removeAll: #( 11 12 ) ] raise: Error",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["removeAll:", "shouldnt:raise:", "assert:", "allSatisfy:", "not", "includes:", "should:raise:"]
+}),
+$globals.SequenceableCollectionTest);
+
 $core.addMethod(
 $core.method({
 selector: "testSecond",

+ 49 - 0
src/Kernel-Tests.st

@@ -623,6 +623,17 @@ testRemoveAll
 	self assert: (self collection removeAll; yourself) equals: self collectionClass new
 !
 
+testRemoveSome
+
+	| original part |
+	
+	original := #( 1 2 3 4 5 6 7 8 9 ).
+	part := #( 2 4 6 ).
+	
+	
+	self assert: (self collection removeAll; yourself) equals: self collectionClass new
+!
+
 testSelect
 	self assert: (self collection select: [ false ]) equals: self collectionClass new.
 	self assert: (self collection select: [ true ]) equals: self collection.
@@ -1167,6 +1178,44 @@ testLastN
 	self should: [ self collection last: 33 ] raise: Error
 !
 
+testRemoveAllFound
+
+	| original part |
+	
+	original := #( 1 2 3 4 5 6 7 8 9 ).
+	part := #( 2 4 6 ).
+	
+	original removeAllFoundIn: part.
+
+	self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.
+	self shouldnt: [ original removeAllFoundIn: #( 11 12 ) ] raise: Error
+!
+
+testRemoveAllSuch
+
+	| original part |
+	
+	original := #( 1 2 3 4 5 6 7 8 9 ).
+	part := #( 2 4 6 ).
+	
+	original removeAllSuchThat: [ :e | part includes: e ].
+
+	self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.
+!
+
+testRemoveAllUsingSome
+
+	| original part |
+	
+	original := #( 1 2 3 4 5 6 7 8 9 ).
+	part := #( 2 4 6 ).
+	
+	original removeAll: part.
+
+	self shouldnt: [ self assert: (part allSatisfy: [ :e | (original includes: e) not ]) ] raise: Error.
+	self should: [ original removeAll: #( 11 12 ) ] raise: Error
+!
+
 testSecond
 	self assert: (self collection second) equals: (self collection at: 2)
 !