MogKit is a toolkit that provides fully tested and easily composable transformations to collections and any series of values (like signals, channels, etc). The transformations are independant of the underlying values or data structures which makes them highly reusable.
As opposed to similar transformation frameworks MogKit works with composition instead of chaining which means the values are only iterated over once instead of once per step.
There are several cases where using MogKit might make sense. Easiest shown with some example.
When you simply want to transform some data in for example an array into a new array.
NSArray *array = @[@1, @2, @3];
NSArray *result = [array mog_transform:MOGMap(^id(NSNumber *number) {
return @(number.intValue + 100);
});
// result is now @[@101, @102, @103]
Or work on some numbers and output as a string:
NSArray *array = @[@1, @2, @3];
NSString *result = MOGTransform(array, MOGStringConcatReducer(@", "), MOGCompose(MOGMap(^id(NSNumber *val) {
return @(val.intValue + 10);
}), MOGMap(^id(NSNumber *val) {
return val.stringValue;
})));
// result = "11, 12, 13"
It can also be used on any NSObject
like:
id object = @10;
NSArray *expected = @[@(-10), @0, @10];
NSArray *result = [object mog_transform:MOGFlatMap(^id(NSNumber *number) {
return @[@(-number.intValue), @0, number];
}) reducer:MOGArrayReducer()];
// result = @[@(-10), @0, @10]
Another case is when you have some data structure and want to add a transformation method to it, for example extending NSArray
and give it a filter
method, all you need to do is
@implementation NSArray (Filterable)
- (NSArray *)filter:(MOGPredicate)predicate
{
return [self mog_transform:MOGFilter(predicate)];
}
@end
Say you want to add a trim:
method to NSArray
that returns a new array with trimSize
elements removed from the start and end.
- (NSArray *)trim:(NSUInteger)trimSize
{
return [self mog_transform:MOGCompose(MOGDrop(trimSize), MOGTake(self.count - 2 * trimSize))];
}
Using MogKit isn't limited to containers implementing NSFastEnumeration
. You can easily make use of it to add composable transformation to anything where you want to transform a set of values. Here is an example adding a -mog_transform:
method to RACStream
(from ReactiveCocoa) in order to apply the passed in transformation to all values on the stream. This can be used instead of chaining a several of RACStreams built in transformations.
@implementation RACStream (MogKit)
- (instancetype)mog_transform:(MOGTransformation)transformation
{
Class class = self.class;
MOGTransformation transformationWithMapToRAC = MOGCompose(transformation, MOGMap(^id(id val) {
return [class return:val];
}));
MOGReducer *reducer = transformationWithMapToRAC(MOGArrayReducer());
return [[self bind:^{
return ^(id value, BOOL *stop) {
id acc = reducer.reduce(reducer.initial(), value, stop);
if (*stop) {
acc = reducer.complete(acc);
}
return [class concat:acc];
};
}] setNameWithFormat:@"[%@] -mog_transform:", self.name];
}
@end
This can later be used to apply a transformation to all values in a channel like this:
NSNumberFormatter *currencyFormatter = [NSNumberFormatter new];
currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
MOGTransformation add100ToIntValuesAndFormatAsCurrency = MOGComposeArray(@[
MOGFilter(StringIsValidInt()),
MOGMap(^id(NSString *string) {
return @([string intValue] + 100);
}),
MOGMap(^id(id val) {
return [currencyFormatter stringFromNumber:val];
})
]);
[[textField.rac_textSignal mog_transform:add100ToIntValuesAndFormatAsCurrency] subscribeNext:^(id x) {
NSLog(@"Number plus 100 = %@", x);
}];
The transformation can then be reused and is not even tied to RACStream
.
- Map
- Filter
- Remove
- Take
- TakeWhile
- TakeNth
- Drop
- DropWhile
- DropNil
- Replace
- ReplaceWithDefault
- MapDropNil
- Unique
- Dedupe
- Flatten
- FlatMap
- PartitionBy
- Partition
- Window
The easiest way is to install through Carthage. Simply add
github "mhallendal/MogKit"
to your Cartfile
and follow the Carthage instructions for including the framework in your application.
Alternatively by using CocoaPods, simply add
pod 'MogKit'
to your Podfile
.
You can also add it as submodule to your project https://github.com/mhallendal/MogKit.git
and include the project file in your application.
If you are using the Foundation extensions, like -mog_transform:
on NSArray
, make sure that you add -ObjC
to your application's Other Linker Flags.
- Swift support (post 1.0)
The API is still not locked down so might change slightly until 1.0 has been released.
For a reader that are familiar with Transducers this will feel familiar, MogKit is implemented using Transducers and MOGTrasformation
s are actually transducers mapping from MOGReducer
to MOGReducer
.
For an introduction to transducers, see Clojure - Transducers and the presentation by Clojure creator Rich Hickey.