Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Object-Oriented .Net primitives. A port of cactoos library by Yegor Bugayenko, author of the "Elegant Objects" books.

License

NotificationsYou must be signed in to change notification settings

icarus-consulting/Yaapii.Atoms

Repository files navigation

Build statuscodecovPRs WelcomeCommitizen friendlyEO principles respected here

Overview

Object-Oriented Primitives for .NET.This is a .NET port of the java libraryCactoos by Yegor Bugayenko.

It follows all the rules suggested in the two "Elegant Objects" books.

Maintainer[s]:

Table Of Contents

Design change Version 1.0 vs 2.0

Caching

Version 1 of Atoms follows the principles of cactoos. All objects in cactoos are so-called live objects. This means, if you create a Text from a urlnew TextOf(new Uri("http://www.google.de")), every call to that object will fetch the content again. There is no caching until you explicitely define it by using a Sticky object. Sticky objects exist for all Types (Text, Enumerable, Map...).

However, after two years of working with Atoms, we realized that developers in our teams tend to think differently. They build objects and use them as if they have been cached. This produces a lot of unnecessary calculations and leads to slowdowns in our apps which we then solve in a second round where we analyze which objects should have been sticky. On the other hand, there are only a few cases where developers really need the live sensing of changes.

This has led to the decision to invert the library caching principle.Atoms 2.0 now has all objects sticky by default. We then introduced newLive Decorators instead. So if you need an object which senses changes, you decorate it using the live decorator:

varexchangeRate=newLive(()=>newTextOf(newUri("https://api.exchangeratesapi.io/latest")));

Live decorators are available for all types.

Caching envelopes

If you want to write your own objects based on Atoms envelopes, you have a switch which you can use to tell the envelope if it should behave as a live object or not (default is no)

publicsealedclassMyLiveTextObject:TextEnvelope{MyLiveTextObject(...):base(...,live:true)}

Exception: Input and output

Input and Output types are not sticky by default.

Shorthand Generics

While Java developers can skip the generic expression in constructors, C# does not allow it. We added shorthand objects to allow skipping it if you use string as generic type, for Enumerables and Maps. You have two objects to make an Enumerable:new ManyOf to get an enumerable of strings andnew ManyOf<T> if you need another type.

There are three objects to make a map:

newMapOf(newKvpOf("Key","Value"));//A map string to stringnewMapOf<int>(newKvpOf<int>("Key",123));//A map string to generic typenewMapOf<int,int>(newKvpOf<int,int>(123,123));//A map generic type to generic type

Envelopes are available for all three map types.

Migration

We decided to not leave the old sticky objects in the new version to get a fresh start.

If you want to migrate from 1.x to 2.x and want to preserve the behaviour regarding sticky objects, you should (in this order):

  • ReplaceScalarOf withLive
  • ReplaceSticky withScalarOf
  • ReplaceManyOf withLiveMany
  • ReplaceStickyEnumerable withManyOf
  • ReplaceListOf withLiveList
  • ReplaceStickyList withListOf
  • ReplaceCollectionOf withLiveCollection
  • ReplaceCollection.Sticky withCollectionOf
  • ReplaceTextOf withLiveText (StickyText did not exist)
  • ReplaceNumberOf withLiveNumber (Stickynumber did not exist)

Maps

Maps are grouped in the Lookup Area of atoms. All map objects implement C#IDictionary<Key, Value> interface.

varmap=newMapOf<string,int>("Amount",100);

Kvp

CSharp maps useKeyValuePair<Key,Value> as contents. These are structs and therefore not objects which we can implement or decorate. Atoms offers the typeIKVp<Key,Value> as alternative.

varmap=newMapOf<string,int>(newKvpOf<string,int>("age",28),newKvpOf<string,int>("height",184),)

Lazy values

This allows maps to accept functions to build the value, and get a simplestrategy pattern

//If you ask the map for "github", it will execute the function and retrieve the content. The content of the first kvp is NOT retrieved.vargithubContent=newMapOf<string,string>(newKvpOf<string,string>("google",()=>newTextOf(newUri("https://www.google.de"))),newKvpOf<string,string>("github",()=>newTextOf(newUri("https://www.github.com"))))["github"];//Note that MapOf is sticky, so if you need live content, decorate the map:varliveContent=newLiveMap<string,string>(()=>newMapOf<string,string>(newKvpOf<string,string>("google",()=>newTextOf(newUri("https://www.google.de"))),newKvpOf<string,string>("github",()=>newTextOf(newUri("https://www.github.com")))))["github"];//Beware: If you have lazy values, you normally do NOT want to execute all functions. Atoms prevents it, so the following will fail:foreach(vardoNotDoThisingithubContent){   ...}//If you know that you need all values, simply enumerate the keys:foreach(varkeyingithubContent.Keys){varvalue=githubContent[key];}

Shorthand Generics

To save typing, there are two shorthand map objects:

newMapOf(newKvpOf("Key","Value"))//Use without generic to get a string-string mapnewMapOf<int>(newKvpOf("Key",100))//Use without generic to get a string-generic map

Shorthand Stringmap

You can make a string-string map by simple writing value pairs one after another:

vartranslations=newMapOf("Objects","Objekte","Functions","Funktionen","Bigmac","Viertelpfünder mit Käse")

Functions

The interfaces are:

//Function with input and outputpublicinterfaceIFunc<In,Out>{OutInvoke(Ininput);}//Function with output onlypublicinterfaceIFunc<Out>{OutInvoke();}//Function with two inputs and one outputpublicinterfaceIFunc<In1,In2,Out>{OutInvoke(In1input1,In2input2);}//Function with input onlypublicinterfaceIAction<In>{voidInvoke(Ininput);}

Execute functions

vari=newFuncOf<int,int>((number)=>number++).Invoke(1);//i will be 2

Cache function output

varurl=newUrl("https://www.google.de");varf=newStickyFunc<Url,IText>((u)=>newTextOf(newInputOf(u))).Invoke(url);varhtml=f.Invoke();//will load the page content from the webvarhtml=f.Invoke();//will load the page content from internal cache

Retry a function

newRetryFunc<int,int>(    input=>{if(newRandom().NextDouble()>input){thrownewArgumentException("May happen");}return0;},100000).Invoke(0.3d);//will try 100.000 times to get a random number under 0.3

Repeat a function

varcount=0;newRepeatedFunc<int,int>(        input=>input++,3).Invoke(count);//will return 3

Use a fallback if a function fails

newFuncWithFallback<string,string>(    name=>{thrownewException("Failure");},    ex=>"Never mind, "+name).Invoke("Miro");//will be "Never mind, Miro"
  • And more

IO Input / Output

The IInput and IOutput interfaces:

publicinterfaceIInput{StreamStream();}publicinterfaceIOutput{StreamStream();}

Input Stream of a file

newInputOf(newUri(@"file:///c:\secret-content-inside.txt")).Stream();//returns a readable stream of the file

Output Stream to a file

newOutputTo(newUri(@"file:///c:\secret-content-inside.txt")).Stream();//returns a readable stream of the file

Output Stream of Console

newConsoleOutput().Stream();//Default console outputnewConsoleErrorOutput().Stream();//Console error output

Read file content as string

varfileContent=newTextOf(newInputOf(newUri(@"file:///c:\secret-content-inside.txt"))).AsString();//reads the content and gives it as text

Read Url page as string

newTextOf(newInputOf(newUrl("https://www.google.de"))).AsString();//gives the content html of the google start page

Read a file and use a fallback if fails

newTextOf(newInputWithFallback(newInputOf(newUri(Path.GetFullPath("/this-file-does-not-exist.txt"))//file to read),newInputOf(newTextOf("Alternative text!"))//fallback to use).AsString();//will be "Alternative Text!"

Get the length of a file

longlength=newLengthOf(newInputOf(newUri(@"file:///c:\great-content-inside.txt"))).Value();//will be the number of bytes in the file
  • Write to file, url, byte arrays, streams

Copy all you are reading from a Stream to a output stream

newLengthOf(newTeeInput("Welcome to the world of c:!",newOutputTo(newUri(@"file:///c:\greeting.txt")))).Value();//will write "Welcome to the world of c:!" to the file c:\greeting.txt.//This happens because TeeInput puts every byte that is read to the specified output.//When calling Value(), every byte is read to count the content.

Copy input/output 1:1 while reading or writing

varinPath=Path.GetFullPath(@"file:///c:\input-file.txt");varoutPath=Path.GetFullPath(@"file:///c:\output-file.txt");newLengthOf(newTeeInput(newInputOf(newUri(inPath)),newOutputTo(newUri(outPath)))).Value();//since LengthOf will read all the content when calling Value(), all that has been read will be copied to the output path.//Alternative: Copy to Console outputnewLengthOf(newTeeInput(newInputOf(newUri(inPath)),newConsoleOutput())).Value();//will dump the read content to output

Cache what you have read

newTextOf(newStickyInput(newInputOf(newUrl("http://www.google.de"))));varhtml1=input.AsString();//will read the url from the webvarhtml2=input.AsString();//will return from the cache

Enumerables

Enumerables use the IEnumerable and IEnumerator interfaces from C#:

publicinterfaceIEnumerable<outT>:IEnumerable{IEnumerator<T>GetEnumerator();}publicinterfaceIEnumerator<outT>:IEnumerator,IDisposable{TCurrent{get;}}

Base Objects

Naming of base objects differs. To save chars, shorthand names are used:

//use without generic and get an IEnumerable<string>varstrings=newManyOf("a string","another string");//use with generic and get an IEnumerable<T>varints=newManyOf<int>(98,20);

Filter

newFiltered<string>(newList<string>(){"A","B","C"},(input)=>input!="B");//will be a list with "A" and "C" inside

Get an item from an enumerable

newItemAt<int>(newManyOf<int>(1,2,3),2).Value();//will be 3 (Zero based)//To get the first item simply do not specify a position:newItemAt<int>(newManyOf<int>(1,2,3)).Value();//will be 1//To get an item with a fallback if it isn't there:Stringfallback="fallback";newItemAt<string>(newManyOf<string>(),//empty list,12,//position 12 which does not existfallback).Value();//will be "fallback"

Sort lists

//Default sorting is forwardnewSorted<int>(newManyOf<int>(3,2,10,44,-6,0));//items will be sorted to -6, 0, 2, 3, 10, 44//Use another comparator for sortingnewSorted<string>(IReverseComparer<string>.Default,//comparator is from C#.NET librarynewManyOf<string>("a","c","hello","dude","Friend"));//will be sorted to hello, Friend, dude, c, a

Count items in enumerables

varl=newLengthOf<int>(newManyOf<int>(1,2,3,4,5)).Value();//will be 5

Map items in an enumerable to another type

ITextgreeting=newItemAt<IText>(newMapped<String,IText>(newManyOf<string>("hello","world","damn"),            input=>newUpperText(newTextOf(input))//is applied to every item and will make a uppertext of it),0).Value();//will be "HELLO"
// Mapping items of a list to another type using index of itemsnewMapped<string,string>(newList<string>(){"One","Two",Three"},(input,index)=>$"{input}={index+1}");// Returns a IEnumerable<string> with Content {"One=1", "Two=2", Three=3"}

Create cycling lists

//here is a list with 3 items and you call the 7th item. The cycled list will not fail but start over when it reaches the end.newItemAt<string>(newCycled<string>(//make a cycled list of the enumerable with 3 itemsnewManyOf<string>("one","two","three")),7).Value();//will be "two"

Join lists together

newLengthOf(newJoined<string>(newManyOf<string>("hello","world","Miro"),newManyOf<string>("how","are","you"),newManyOf<string>("what's","up"))).Value();//will be 8

Limit lists

newSumOfInts(newHeadOf<int>(newManyOf<int>(0,1,2,3,4),3)).Value();//will be 3 (0 + 1 + 2)

Cache list contents

//this snippet has an endless list, which then is limited to the size. Every time someone calls the list, size increases and the list would grow. But StickyEnumerable prevents that and always returns the same list.intsize=2;varlist=newStickyEnumerable<int>(newHeadOf<int>(newEndless<int>(1),newScalarOf<int>(()=>Interlocked.Increment(refsize))));newLengthOf(list).Value();//will be 2newLengthOf(list).Value();//will be 2
  • and more

Scalar

The IScalar interface looks like this:

publicinterfaceIScalar<T>{TValue();}

A scalar is an object which can encapsulate objects and functions that return objects. It enables you to let a function appear as its return value. This is very useful to keep constructors code-free but also keep your overall object count low.

Also, scalars can be used to perform logical operations like And, Or, Not and more on function results or objects.

Encapsulate objects

varsc1=newScalarOf<string>("this brave string");stringstr=sc.Value();//returns the stringvarsc2=newScalarOf<IEnumerable<int>>(newManyOf<int>(1,2,3,4,5));IEnumerable<int>lst=sc2.Value();//returns the list

Encapsulate functions

varsc=newScalarOf<string>(()=>newTextOf(newInputOf(newUrl("http://www.ars-technica.com"))).AsString());stringhtml=sc.Value();//will fetch the html from the url and return it as a string

Cache function results

varsc=newStickyScalar<string>(()=>newTextOf(newInputOf(newUrl("http://www.ars-technica.com"))).AsString()).Value();stringhtml=sc.Value();//will fetch the html from the url and return it as a stringstringhtml2=sc.Value();//will return the html from the cache

Logical And

varresult=newAnd<True>(()=>true,()=>false,()=>true).Value();//will be falsevarnumber=3;newAnd<True>(()=>true,//function that returns true()=>number==4//function that returns false).Value();//will be false//you can also pass scalars into AND, and more.

Logical ternary

newTernary<bool,int>(newTrue(),//condition is true6,//if true16//if false).Value();//will be 6newTernary<int,int>(5,//input to test    input=>input>3,//condition    input=>input=8,//return if condition true    input=>input=2//return if condition false).Value();//will be 8

And more...

  • Negative
  • Max
  • Min
  • Or

Text

The IText interface looks like this:

publicinterfaceIText:IEquatable<IText>{StringAsString();}

Transform text

//Lower a textnewLowerText(newTextOf("HelLo!")).AsString();//will be "hello!"//upper a textnewUpperText(newTextOf("Hello!")).AsString();//will be "HELLO!"

Reverse text

newReversedText(newTextOf("Hello!")).AsString();//"!olleH"

Trim text

newTrimmedText(newTextOf("  Hello!\t ")).AsString();// "Hello!"newTrimmedLeftText(newTextOf("  Hello!\t ")).AsString();// "Hello!   \t "newTrimmedRightText(newTextOf("  Hello!\t ")).AsString();// "  Hello!"

Split text

IEnumerable<Text>splitted=newSplitText("Hello world!","\\s+");

Replace text

newReplacedText(newTextOf("Hello!"),"ello",// what to replace"i"// replacement).AsString();// "Hi!"

Join texts

newJoinedText(" ","hello","world").AsString();// "hello world"

Format texts with arguments

newFormattedText("{0} Formatted {1}",1,"text").AsString();// "1 Formatted text"

Convert from and to text

//text from a string with encodingvarcontent="Greetings, Mr.Freeman!";newTextOf(content,Encoding.UTF8);//text from a input with default encodingvarcontent="Hello, my precious coding friend! with default charset";newTextOf(newInputOf(content));//text from a StringReaderStringsource="Hello, Dude!";newTextOf(newStringReader(source),Encoding.UTF8);//text from a char arraynewTextOf('O',' ','q','u','e',' ','s','e','r','a',' ','q','u','e',' ','s','e','r','a');//text from a byte arraybyte[]bytes=newbyte[]{(byte)0xCA,(byte)0xFE};newTextOf(bytes);//text from a StringBuilderStringstarts="Name it, ";Stringends="then it exists!";Assert.True(newTextOf(newStringBuilder(starts).Append(ends));//text from an exceptionnewTextOf(newIOException("It doesn't work at all"));

LinQ Analogy

Standard Query Operators

LinQYaapii.Atoms
AggregateNot available yet
AllAnd<T>
AnyOr<T>
AsEnumerable
var arr = new int[]{ 1, 2, 3, 4 };
var enumerable = new ManyOf<int>(arr);
Average
var avg = new AvgOf(1, 2, 3, 4).AsFloat(); //avg = 2.5
CastNot available yet
Concat
var joined = new Joined<string>(
  new ManyOf<string>("dies", "ist"),
  new ManyOf<string>("ein", "Test")
).Value(); //joined = {"dies", "ist", "ein", "Test"}
Contains
var b = new Contains<string>(
  new ManyOf<string>("Hello", "my", "cat", "is", "missing"),
  (str) => str == "cat"
).Value()); //b = true
Count
var length = new LengthOf<int>(
  new ManyOf<int>(1, 2, 3, 4, 5)
).Value(); //length will be 5
DefaultIfEmptyNot available yet
Distinct
var dis = new Distinct<int>(
  new ManyOf<int>(1, 2, 3),
  new ManyOf<int>(10, 2, 30)
).Value() // dis = {1, 2, 3, 10, 30}
//actual with bug
ElementAt
var itm = new ItemAt<int>(
  new ManyOf<int>(0,2,5),
   2
).Value() //itm = 2
ElementAtOrDefault
var itm = new ItemAt<string>(
  new ManyOf<string>(),
  12,
  fallback
).Value() // itm = "fallback"
Empty
new EnmuerableOf<int>()
ExceptNot available yet
First
var list = new EnumerableO<int>(1, 2, 3);
 var first = new ItemAt<int>(list).Value();
 // first = 1
FirstOrDefault
var itm = new ItemAt<string>(
  new ManyOf<string>(),
  1,
  fallback
).Value() // itm = "fallback"
Foreach
var list = new List[];
new Each<int>(
  (i) => lst[i] = i,
  0,1,2
).Invoke(); //eachlist = {0,1,2}
GroupByNot available yet
GroupJoinNot available yet
IntersectNot available yet
JoinNot available yet
Last
var last = new ItemAt<int>(
  new Reversed<int>(
    new ManyOf(5, 6 ,7 ,8)
  )
).Value() // last = 8
LastOrDefault
var itm = new ItemAt<string>(
  new Reversed<string>(
    new ManyOf<string>()
  ),
  1,
  fallback
).Value() // itm = "fallback"
LongCountNot available yet*
Max
var max = new MaxOf(22, 2.5, 35.8).AsDouble(); //max = 35.8; .AsInt() = 35
Min
var max = new MaxOf(22, 2.5, 35.8).AsDouble(); //max = 2.5; .AsInt() = 2
OfTypeNot available yet
OrderBy
var sorted = new Sorted<int>(
  new ManyOf<int>(3, 2, 10, 44, -6, 0)
) //sorted = {-6, 0, 2, 3, 10, 44
OrderByDescending
var sorted = new Sorted<string>(
  IReverseComparer<string>.Default,
  new ManyOf<string>("a", "c", "hello", "dude", "Friend")
) //sorted = {hello, Friend, dude, c, a}
RangeNot available yet
Repeat
var repeated = new Repeated<int>(10,5) // repeated = {10, 10, 10, 10, 10}
Reverse
 var reversed = new Reversed<int>(ManyOf(2,3,4)); //reversed = {4,3,2}
Select
var selected = Mapped<string,string>(
  new List<string>() {"One", "Two", Three"},
  (tStr, index) => $"{tStr}={index+1}"
)// selected = {"One=1", "Two=2", Three=3"}
SelectManyNot available yet
SequenceEqualNot available yet
SingleNot available yet
SingleOrDefaultNot available yet
Skip
var skipped = new Skipped<string>(
  new ManyOf<string>("one", "two", "three", "four"),
  2
) // skipped = {three, four}
SkipWhileNot available yet
Sum
var sum = new SumOf(
  1.5F, 2.5F, 3.5F
).AsFloat() //sum = 7.5
Take
var lmt = new HeadOf<int>(
  new ManyOf<int>(0, 1, 2, 3, 4),
  3
)//lmt = {0, 1, 2}
TakeWhileNot available yet
ThenByNot available yet
ThenByDescendingNot available yet
ToArrayNot available yet
ToDictionary
var dic = new MapOf(
  new Enumerable<KeyValuePair<string,string>>(
    new KyValuePair<string,string>("key","value"),
    new KyValuePair<string,string>("key2", "value2")
  )
) // dic = {{key, value}{key2, value2}}
ToList
var list = new CollectionOf<int>(
  new ManyOf<int>(1,2,3,4)
);
ToLookupNot available yet
Union
var enu = new Distinct<int>(
  new Joined<int>(
    new ManyOf<int>(1,2,3,4),
    new ManyOf<int>(3,4,5,6)
  ).Value()
).Value(); //enu ={1,2,3,4,5,6}
Where
var newFiltered = new Filtered<string>(
  new List<string>() { "A", "B", "C" },
  (input) => input != "B"
); //newFiltered contains A & C
ZipNot available yet

About

Object-Oriented .Net primitives. A port of cactoos library by Yegor Bugayenko, author of the "Elegant Objects" books.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp