ImportedLinq
provides utility collection methods that are imported from other programming languages.
LINQ to Objects
is one of the best language feature in C#. But LINQ to Objects
does not have some methods and features.
For example,
MaxBy
finds the elements that have maximum key value in the sequence by using the default comparer.Buffer
generates a sequence of buffers over the source sequence with specified length.Flatten
converts sequence of sequence to sequence.IsEmpty
determines whether sequence is empty or not.
These methods are common in other programming languages.
ImportedLinq
consists of LINQ like extension methods that are imported from other programming language such MaxBy
, Buffer
, Flatten
and IsEmpty
.
ImportedLinq
provide collections methods that are common in other programming language to C# programmer. And ImportedLinq
helps programmers who are new to C# but have experience to other programming language.
ImportedLinq
is beta version. Breaking changes may be occur.
ImportedLinq
needs .NET Standard 1.0.
If you use ImportedLinq
in Unity, Please download unitypackage
from release page
- Unity : download
unitypackage
from release page and import it. - Others : add your project as NuGet packages.
Add using directive like next code.
using ImportedLinq;
Like LINQ methods, use as extension method for IEnumerable<T>
.
IReadOnlyList<Monster> monsters = LoadMonsters();
IReadOnlyCollection<Monster> maxLevelMonsters = monsters.MaxBy(it => it.Level);
IEnumerable<IReadOnlyList<Monster>> bufferedMonsters = monsters.Buffer(8);
There are some great collection method libraries for C#.
This section describes difference between them and ImportedLinq
.
Please note that ImportedLinq
provides utility collection methods that are imported from other Languages. This is ImportedLinq
's motto.
The Interactive Extensions (Ix) is a .NET library which extends LINQ to Objects to provide many of the operators available in Rx but targeted for IEnumerable
from Ix.NET section description.
Ix.NET
provides many utility methods for IEnumerable<T>
. But please note that these methods are correspond methods to Rx.NET
methods.
LINQ to Objects is missing a few desirable features.
This project enhances LINQ to Objects with extra methods, in a manner which keeps to the spirit of LINQ.
from MoreLINQ's README top content.
MoreLINQ
is great C# collection method library and has many extension collection methods for IEnumerable<T>
. Some of them are overlapped with ImportedLinq
.
But MoreLINQ
does not have methods whose implementation are so simple like IsEmpty
and flatten
. See this issue.
And there are so many useful methods in MoreLINQ
. Compare MoreLINQ
, ImportedLinq
provides only methods that are common in other programming languages.
Show example method usage with next code.
enum Direction
{
Up, Right, Down, Left,
}
enum MonsterType
{
Grass, Fire, Water,
}
struct Monster
{
public int Level { get; }
public MonsterType MonsterType { get; }
public Monster(int level, MonsterType monsterType) => (Level, MonsterType) = (level, monsterType);
}
struct Position
{
public int X { get; }
public int Y { get; }
public Position(int x, int y) => (X, Y) = (x, y);
public static Position Move(Position position, Direction direction)
{
switch (direction)
{
case Direction.Up: return new Position(position.X, position.Y + 1);
case Direction.Right: return new Position(position.X + 1, position.Y);
case Direction.Down: return new Position(position.X, position.Y - 1);
case Direction.Left: return new Position(position.X - 1, position.Y);
default: throw new Exception();
}
}
}
Finds the elements that have maximum / minimum key value in the sequence by using the default comparer.
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IReadOnlyCollection<Monster> expected = new List<Monster>
{
new Monster(55, MonsterType.Fire),
};
IReadOnlyCollection<Monster> actual = source.MaxBy(it => it.Level);
Assert.Equal(expected, actual);
Generates a sequence of buffers over the source sequence with specified length.
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IEnumerable<IReadOnlyList<Monster>> expected = new[]
{
new[]
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
},
new[]
{
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
},
new[]
{
new Monster(36, MonsterType.Water),
},
};
IEnumerable<IReadOnlyList<Monster>> actual = source.Buffer(3);
Assert.Equal(expected, actual);
Converts sequence of sequence to sequence.
IEnumerable<IEnumerable<Monster>> source = new List<Monster[]>
{
new[]
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
},
new[]
{
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
},
new[]
{
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
},
};
IEnumerable<Monster> expected = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IEnumerable<Monster> actual = source.Flatten();
Assert.Equal(expected, actual);
Determines whether sequence is empty or not.
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
Assert.False(source.IsEmpty());
Creates ValueTuple(First, Second) to the corresponding elements of two sequences, produces a sequence of the ValueTuple(First, Second).
IEnumerable<Monster> sourceMonsters = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IEnumerable<Position> sourcePositions = new List<Position>
{
new Position(0, 1),
new Position(1, 3),
new Position(4, -1),
};
IEnumerable<(Monster First, Position Second)> actual = sourceMonsters.Zip(sourcePositions);
IEnumerable<(Monster First, Position Second)> expected = new List<(Monster First, Position Second)>
{
(new Monster(5, MonsterType.Grass), new Position(0, 1)),
(new Monster(16, MonsterType.Grass), new Position(1, 3)),
(new Monster(32, MonsterType.Grass), new Position(4, -1)),
};
Assert.Equal(expected, actual);
Creates ValueTuple(Element, Index) to the corresponding element and its index, produces sequence of the ValueTuple(Element, Index).
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IEnumerable<(Monster Element, int Index)> actual = source.WithIndex();
IEnumerable<(Monster Element, int Index)> expected = new List<ValueTuple<Monster, int>>
{
ValueTuple.Create(new Monster(5, MonsterType.Grass), 0),
ValueTuple.Create(new Monster(16, MonsterType.Grass), 1),
ValueTuple.Create(new Monster(32, MonsterType.Grass), 2),
ValueTuple.Create(new Monster(5, MonsterType.Fire), 3),
ValueTuple.Create(new Monster(55, MonsterType.Fire), 4),
ValueTuple.Create(new Monster(5, MonsterType.Water), 5),
ValueTuple.Create(new Monster(36, MonsterType.Water), 6),
};
Assert.Equal(expected, actual);
- Groovy : withIndex
- Scala : zipWithIndex
- Kotlin : withIndex
Generates a accumulated values sequence with scanning the source sequence and applying the accumulator function.
IEnumerable<Direction> directions = new[]
{
Direction.Down,
Direction.Down,
Direction.Left,
Direction.Up,
Direction.Right,
Direction.Up
};
Position seed = new Position(0, 0);
IEnumerable<Position> actual = directions.Scan(seed, Position.Move);
IEnumerable<Position> expected = new[]
{
new Position(0, -1),
new Position(0, -2),
new Position(-1, -2),
new Position(-1, -1),
new Position(0, -1),
new Position(0, 0),
};
Assert.Equal(expected, actual);
- Scala : scan
Partitions a sequence to Value(True, False) with a predicate, True is satisfied and False is not.
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IEnumerable<Monster> expectedOver30Level = new List<Monster>
{
new Monster(32, MonsterType.Grass),
new Monster(55, MonsterType.Fire),
new Monster(36, MonsterType.Water),
};
IEnumerable<Monster> expectedUnderOrEqual30Level = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(5, MonsterType.Water),
};
(IReadOnlyCollection<Monster> actualOver30Level, IReadOnlyCollection<Monster> actualUnderOrEqual30Level)
= source.Partition(it => it.Level > 30);
Assert.Equal(expectedOver30Level, actualOver30Level);
Assert.Equal(expectedUnderOrEqual30Level, actualUnderOrEqual30Level);
Creates a Dictionary<TKey, int> from an source sequence according to a specified key selector function and exist counts in source sequence.
IEnumerable<Monster> source = new List<Monster>
{
new Monster(5, MonsterType.Grass),
new Monster(16, MonsterType.Grass),
new Monster(32, MonsterType.Grass),
new Monster(5, MonsterType.Fire),
new Monster(55, MonsterType.Fire),
new Monster(5, MonsterType.Water),
new Monster(36, MonsterType.Water),
};
IReadOnlyDictionary<MonsterType, int> expected = new Dictionary<MonsterType, int>
{
{MonsterType.Grass, 3},
{MonsterType.Fire, 2},
{MonsterType.Water, 2},
};
IReadOnlyDictionary<MonsterType, int> actual = source.CountBy(it => it.MonsterType);
Assert.Equal(expected, actual);
- Groovy : countBy
If you find bug or problem, please create issue in English.
If you have feature request, please create issue with link to other programming language collection method's document in English.
Ryota Murohoshi is game Programmer in Japan.
- Posts:http://qiita.com/RyotaMurohoshi (JPN)
- Twitter:https://twitter.com/RyotaMurohoshi (JPN)
This library is under MIT License.