✅ how do i join two lists?

i have 4 lists i want to join together. the plus operator doesnt work for this, even when converting to arrays.
22 Replies
GenTAO[τ, τ]
GenTAO[τ, τ]6mo ago
u can use the "Addrange" method List<int> list1 = new List<int> { 1, 2, 3 }; List<int> list2 = new List<int> { 4, 5, 6 }; List<int> list3 = new List<int> { 7, 8, 9 }; List<int> list4 = new List<int> { 10, 11, 12 }; List<int> result = new List<int>(); result.AddRange(list1); result.AddRange(list2); result.AddRange(list3); result.AddRange(list4); like this
becquerel
becquerel6mo ago
or .Concat
Kouhai
Kouhai6mo ago
Or using collection expression
becquerel
becquerel6mo ago
var total = list1.Concat(list2).Concat(list3).ToList();
slowly losing it
slowly losing itOP6mo ago
whats that
Kouhai
Kouhai6mo ago
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions For your use case you'd do
List<string> total = [..list1, ..list2, ..list3, ..list4];
List<string> total = [..list1, ..list2, ..list3, ..list4];
slowly losing it
slowly losing itOP6mo ago
im making a chess engine and someone said this was really expensive
Kouhai
Kouhai6mo ago
:ConfusedAnime: Expensive in what sense? Yes this would allocate a new List, so if you want minimal allocation you might want something else, but any time you're creating a new List you have to allocate
slowly losing it
slowly losing itOP6mo ago
no idea in what sense. i picked this language up like a week ago and learnt the basics in 2 days so i have no idea about whats better than what :shrug:
Kouhai
Kouhai6mo ago
Hmm, can you show the code where you'd need to join the lists?
slowly losing it
slowly losing itOP6mo ago
public static List<Move> GenerateWhitePawnMoves(ulong occupancy, ulong pawnBitboard)
{
PawnMoves pawnMoves = GetWhitePawnMoves(occupancy, pawnBitboard);

List<Move> left_attacks = GenerateMovesWithOffset(pawnMoves.LeftAttacks, 7);
List<Move> right_attacks = GenerateMovesWithOffset(pawnMoves.RightAttacks, 9);

List<Move> single_pushes = GenerateMovesWithOffset(pawnMoves.SinglePushForward, 8);
List<Move> double_pushes = GenerateMovesWithOffset(pawnMoves.DoublePushForward, 16);

List<Move> nextMoves = [..left_attacks, ..right_attacks, ..single_pushes, ..double_pushes];

return nextMoves;
}
public static List<Move> GenerateWhitePawnMoves(ulong occupancy, ulong pawnBitboard)
{
PawnMoves pawnMoves = GetWhitePawnMoves(occupancy, pawnBitboard);

List<Move> left_attacks = GenerateMovesWithOffset(pawnMoves.LeftAttacks, 7);
List<Move> right_attacks = GenerateMovesWithOffset(pawnMoves.RightAttacks, 9);

List<Move> single_pushes = GenerateMovesWithOffset(pawnMoves.SinglePushForward, 8);
List<Move> double_pushes = GenerateMovesWithOffset(pawnMoves.DoublePushForward, 16);

List<Move> nextMoves = [..left_attacks, ..right_attacks, ..single_pushes, ..double_pushes];

return nextMoves;
}
public struct Move
{
public int src;
public int dst;
}

public struct PawnMoves
{
public ulong LeftAttacks;
public ulong RightAttacks;
public ulong SinglePushForward;
public ulong DoublePushForward;
}
public struct Move
{
public int src;
public int dst;
}

public struct PawnMoves
{
public ulong LeftAttacks;
public ulong RightAttacks;
public ulong SinglePushForward;
public ulong DoublePushForward;
}
here are the structs too done everything :thumbsup:
GenTAO[τ, τ]
GenTAO[τ, τ]6mo ago
public static List<Move> GenerateWhitePawnMoves(ulong occupancy, ulong pawnBitboard) { PawnMoves pawnMoves = GetWhitePawnMoves(occupancy, pawnBitboard); List<Move> left_attacks = GenerateMovesWithOffset(pawnMoves.LeftAttacks, 7); List<Move> right_attacks = GenerateMovesWithOffset(pawnMoves.RightAttacks, 9); List<Move> single_pushes = GenerateMovesWithOffset(pawnMoves.SinglePushForward, 8); List<Move> double_pushes = GenerateMovesWithOffset(pawnMoves.DoublePushForward, 16); // Create a new list and add all the other lists into it List<Move> nextMoves = new List<Move>(); nextMoves.AddRange(left_attacks); nextMoves.AddRange(right_attacks); nextMoves.AddRange(single_pushes); nextMoves.AddRange(double_pushes); return nextMoves; } private static List<Move> GenerateMovesWithOffset(ulong bitboard, int offset) { List<Move> moves = new List<Move>(); while (bitboard != 0) { int src = BitScanForward(bitboard); // Find the index of the least significant bit set to 1 int dst = src + offset; moves.Add(new Move { src = src, dst = dst }); bitboard &= bitboard - 1; // Clear the least significant bit } return moves; } private static int BitScanForward(ulong bitboard) { return (int)(Math.Log(bitboard & ~(bitboard - 1), 2)); }
slowly losing it
slowly losing itOP6mo ago
cant read this without code blocks
GenTAO[τ, τ]
GenTAO[τ, τ]6mo ago
ok,,i ll provide
Kouhai
Kouhai6mo ago
You could use AddRange but it'll still allocate, if you want to reduce allocations, you might wanna use ArrayPool
Anton
Anton6mo ago
Do you need it to be a list exactly? if you just need to iterate it, there's a way to do this without allocations if you already have the other lists
slowly losing it
slowly losing itOP6mo ago
i'd hope so how do i do that? how do i do that? sorry for late response
Anton
Anton6mo ago
You can allow a type to be for-looped over by means of structural typing. You have to wrap all the lists in a struct, have it implement GetEnumerator and the enumerator struct, which loops through all lists in order you basically have to do an iterator state machine that loops through each list item-by-item IEnumerable is more convenient + it's lazily enumerated, but it does allocate memory for the iterators. If you implement these things manually with a struct, you don't need to heap allocate IEnumerable (linq / yield return) makes the state machine for you
slowly losing it
slowly losing itOP6mo ago
what would this look like in code?
Anton
Anton6mo ago
look it up in the docs
Kouhai
Kouhai6mo ago
Couple of things you can do to reduce allocations are 1. Rent an array from ArrayPool so something like ArrayPool<Move>.Shared.Rent(COUNT_GOES_HERE) this will return an array Move[] with at least COUNT_GOES_HERE capactiy, after you're done using the you simply ArrayPool<Move>.Shared.Return(RENTED_ARRAY_GOES_HERE) 2. Make another version of GenerateMovesWithOffset that takes in a Span<Move> and writes the results to said span, although you need to make sure you have ways of informing the caller how many Moves have been added to the span Whether it's worth it to do any of these optimizations depends on how frequent GenerateWhitePawnMoves and GenerateMovesWithOffset get called
slowly losing it
slowly losing itOP6mo ago
ok got it. thank you :thumbsup:

Did you find this page helpful?