C# Pattern match arrays - c#

var x = new int[] { 1, 2 };
var y = x switch {
{ 1, 2 } => "yea",
_ => "nay"
};
fails to compile.
How can I pattern-match arrays?

You have to expand the elements of the array yourself like so
var x = new int[] { 1, 2 };
var y = (x[0], x[1]) switch {
(1, 2) => "yea",
_ => "nay"
};

Related

How to split object strings to array of object?

I have a c# object:
var obj = new Item { xcoords="1,2,3", ycoords="5,6,7", zcoords="8,9,4" }
So I want to split this into an array like following:
[
new Item2 { x=1, y=5, z=8 },
new Item2 { x=2, y=6, z=9 },
new Item2 { x=3, y=7, z=4 }
]
Can I do this using Linq? or another way in c#?
Let's create a helper method (either as separate method or as local function):
static IEnumerable<int> ToInts(string s) => s.Split(',').Select(p => Int32.Parse(p));
Putting things together:
var obj = new Item { xcoords = "1,2,3", ycoords = "5,6,7", zcoords = "8,9,4" };
var result = ToInts(obj.xcoords)
.Zip(ToInts(obj.ycoords), ToInts(obj.zcoords))
.Select(t => new Item2{ x = t.First, y = t.Second, z = t.Third });
Note: this overload of Zip (since .NET 6?, the doc is wrong on this point) produces tuples with 3 elements named First, Second and Third.
You can append a .ToArray() if you need an array of Coord.
Test assuming that ToString is overridden like this in Item2:
public override string ToString() => $"[{x}, {y}, {z}]";
foreach (var item in result) {
Console.WriteLine(item);
}
yields:
[1, 5, 8]
[2, 6, 9]
[3, 7, 4]
You don't need LINQ here if coords number is static.
var obj = new Item { xcoords="1,2,3", ycoords="5,6,7", zcoords="8,9,4" };
var xcoords = obj.xcoords.Split(',');
var ycoords = obj.ycoords.Split(',');
var zcoords = obj.zcoords.Split(',');
var result = new Item2[]
{
new Item2 { x = int.Parse(xcoords[0]), y = int.Parse(ycoords[0]), z = int.Parse(zcoords[0]) },
new Item2 { x = int.Parse(xcoords[1]), y = int.Parse(ycoords[1]), z = int.Parse(zcoords[1]) },
new Item2 { x = int.Parse(xcoords[2]), y = int.Parse(ycoords[2]), z = int.Parse(zcoords[2]) },
};
If coords number is dynamic but same for x, y and z then simple "for" loop may resolve the problem.
var obj = new Item { xcoords="1,2,3,4", ycoords="5,6,7,8", zcoords="8,9,4,10" };
var xcoords = obj.xcoords.Split(',');
var ycoords = obj.ycoords.Split(',');
var zcoords = obj.zcoords.Split(',');
var result = new Item2[xcoords.Length];
for (var i = 0; i < xcoords.Length; i++)
{
result[i] = new Item2
{
x = int.Parse(xcoords[i]),
y = int.Parse(ycoords[i]),
z = int.Parse(zcoords[i])
};
}
LINQ version (Requires .NET 6 or higher. In the previous versions Zip method takes only 2 collections at once.):
var obj = new Item { xcoords = "1,2,3,4", ycoords = "5,6,7,8", zcoords = "8,9,4,10" };
var result = Enumerable
.Zip(
obj.xcoords.Split(','),
obj.ycoords.Split(','),
obj.zcoords.Split(','),
(x, y, z) => (x, y, z))
.Select(
coord => new Item2
{
x = int.Parse(coord.x),
y = int.Parse(coord.y),
z = int.Parse(coord.z)
});
Linq approach https://dotnetfiddle.net/CHizmw
Item obj = new Item { xcoords = "1,2,3", ycoords = "5,6,7", zcoords = "8,9,4" };
Func<string, int[]> GetInt = value => value.Split(',').Select(int.Parse).ToArray();
var helper = new { xc = GetInt(obj.xcoords), yc = GetInt(obj.ycoords), zc = GetInt(obj.zcoords) };
Item2[] obj2 = Enumerable.Range(0, helper.xc.Length).Select(o => new Item2() { x = helper.xc[o], y = helper.yc[o], z = helper.zc[o] }).ToArray();

Change big collection into unique List of collections

I have a big list of objects and in this object there is a category ID something like:
var list = new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 1, Value = new { }},
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 3, Value = new { }}
// and so on
};
So I am looking for making this complicated list more organized like list of lists of unique elements
something like:
var result = new List<List<Example>>
{
new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 3, Value = new { }}
},
new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }}
},
new List<Example>
{
new Example {CatId = 1, Value = new { }}
}
}
Problem is I do not what to use, group by will not fix my case, so how to do this in most efficient way.
So this is about partitioning, it's the sort of thing that is easy to do in a database query, but in c# you need to create some key with a partition number that you can then use to .GroupBy.
The partitioning itself is a grouping
var projected = list.GroupBy(x => x.CatId)
.SelectMany( g => g.Select( ( x, i ) => new { Item = x, rn = i + 1 } ) );
This gives you records that look like:
{"Item":{"CatId":1,"Value":{}},"rn":1}
{"Item":{"CatId":1,"Value":{}},"rn":2}
{"Item":{"CatId":1,"Value":{}},"rn":3}
{"Item":{"CatId":2,"Value":{}},"rn":1}
{"Item":{"CatId":2,"Value":{}},"rn":2}
{"Item":{"CatId":3,"Value":{}},"rn":1}
As you can see that rn ("row number") value can be used to group by:
var result = projected.GroupBy(x => x.rn, x => x.Item);
This gives us:
[{"CatId":1,"Value":{}},{"CatId":2,"Value":{}},{"CatId":3,"Value":{}}]
[{"CatId":1,"Value":{}},{"CatId":2,"Value":{}}]
[{"CatId":1,"Value":{}}]
So, all in 1 go:
var result = list.GroupBy(x => x.CatId)
.SelectMany( g => g.Select( ( x, i ) => new { Item = x, rn = i + 1 } ) )
.GroupBy(x => x.rn, x => x.Item);
Live example: https://dotnetfiddle.net/AlTfk8

Group with inside an array element using LINQ

For example:
var a = new { Key = new[] {1, 2} ... };
var b = new { Key = new[] {2, 3} ... };
var c = new { Key = new[] {3} ... };
I want a LINQ query that will get the result below, after new [] {a, b, c}.GroupByWithArray(x => x.Key)
Key 1: a
Key 2: a, b
Key 3: b, c
var a = new { Key = new[] { 1, 2 }};
var b = new { Key = new[] { 2, 3 }};
var c = new { Key = new[] { 3 }};
var result =
new[] { a, b, c }.SelectMany(item => item.Key.Select(key => (Item: item, Key: key)))
.GroupBy(pair => pair.Key)
.ToDictionary(group => group.Key,
group => group.Select(pair => pair.Item).ToArray());
var expected = new Dictionary<int, object[]>
{
{ 1, new[] { a }},
{ 2, new[] { a, b }},
{ 3, new[] { b, c }}
};
result.Should().BeEquivalentTo(expected); // Pass Ok
This seems the most logical to what you want:
var a = new { Key = new[] { 1, 2 } };
var b = new { Key = new[] { 2, 3 } };
var c = new { Key = new[] { 3 } };
var values = new[] { 'a', 'b', 'c' };
var result =
new[] { a, b, c }
.Zip(values, (keys, value) => new { keys, value })
.SelectMany(x => x.keys.Key, (x, key) => new { key, x.value })
.GroupBy(x => x.key, x => x.value);
var output =
String.Join(
Environment.NewLine,
result.Select(r => $"Key {r.Key}: {String.Join(", ", r)}"));
That gives:
Key 1: a
Key 2: a, b
Key 3: b, c
Now, I don't know what you meant by the ... in your question, but if I just assume you meant that the list can be longer, then that doesn't change anything.

Converting a 2d array into a 2d array of a different type. int[,] => ushort[,]

Im trying to find out a way to convert a 2d array of one type to another in a single line of code.
This is a personal learning experience rather than a need to do it in one line!!
Ive gotten so far as to convert it into IEnumerable<Tuple<ushort,ushort>>. Not sure where to go from here.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var Result = (from e in X.OfType<int>() select e)
.Select(S => (ushort)S)
.Select((value, index) => new { Index = index, Value = value })
.GroupBy(x => x.Index / 2)
.Select(g => new ushort[,] { { g.ElementAt(0).Value,
g.ElementAt(1).Value } });
Need to somehow convert the collection of Tuples into a ushort[,]
EDIT:
Just clarifying the question.
How do I convert a int 2d array into a ushort 2d array using a single line of code in linq?
EDIT:
Ive updated my code.
I now have it resulting in a IEnumerable collection of ushort[,].
I need to now find a way to concatonate all these into a single ushort[,]
The best I could come up with to keep the result two dimensional is this:
var input = new [,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var output = new ushort[input.GetUpperBound(0) + 1, input.GetUpperBound(1) + 1];
Buffer.BlockCopy(input.Cast<int>().Select(x => (ushort)x).ToArray(), 0, output, 0, input.GetLength(0) * input.GetLength(1) * sizeof(ushort));
Using explicit cast to ushort we could do this, I'm leaving it to you to explore consequences in the conversion and addressing them.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
ushort[,] shortArray = new ushort[X.GetUpperBound(0)+1, X.GetUpperBound(1)+1];
for (int i = 0; i <= X.GetUpperBound(0); ++i)
{
for(int j=0;j<= X.GetUpperBound(1);j++)
shortArray[i, j] = (ushort)X[i,j];
}
In case you are interested on Jagged array instead of multidimensional array, use this.
var jagged = X.Cast<int>()
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / (X.GetUpperBound(1) +1))
.Select(x => x.Select(s=> (ushort)s.Value).ToArray())
.ToArray();
Working example
How about:
var Result = X.OfType<int>().Select(s => new { Index = (s + 1) / 2, Value = s})
.GroupBy(g => g.Index)
.Select(s => s.Select(g => (ushort)g.Value).ToArray())
.ToArray();

How to group items by index? C# LINQ

Suppose I have
var input = new int[] { 0, 1, 2, 3, 4, 5 };
How do I get them grouped into pairs?
var output = new int[][] { new int[] { 0, 1 }, new int[] { 2, 3 }, new int[] { 4, 5 } };
Preferably using LINQ
input
.Select((value, index) => new { PairNum = index / 2, value })
.GroupBy(pair => pair.PairNum)
.Select(grp => grp.Select(g => g.value).ToArray())
.ToArray()
Probably not applicable to you, but you could use the new Zip method in C# 4.0
var input = new int[] { 0, 1, 2, 3, 4, 5 };
IEnumerable evens = input.Where((element, index) => index % 2 == 0);
IEnumerable odds = input.Where((element, index) => index % 2 == 1);
var results = evens.Zip(odds, (e, o) => new[] { e, o }).ToArray();
var indexedNumbers = input.Select((number, index) => new { Index = index, Number = number });
var pairs =
from indexedNumber in indexedNumbers
group indexedNumber by indexedNumber.Index / 2 into indexedNumberPair
select indexedNumberPair.Select(indexedNumber => indexedNumber.Number);
var arrays = pairs.Select(pair => pair.ToArray()).ToArray();
Using ToLookup method:
input
.Select((number, index) => new { index , number})
.ToLookup(_ => _.index / 2, _ => _.number)
.Select(_ => _.ToArray())
.ToArray();
Using Zip method:
input
.Zip(input.Skip(1), (_, __) => new[] {_, __})
.Where((_, index) => index % 2 == 0)
.ToArray();

Categories