✅ 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
!τ$TAo_mickey[τ, τ]
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
Becquerel3mo ago
or .Concat
Kouhai
Kouhai3mo ago
Or using collection expression
Becquerel
Becquerel3mo ago
var total = list1.Concat(list2).Concat(list3).ToList();
it’s raining outside
whats that
Kouhai
Kouhai3mo 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];
it’s raining outside
im making a chess engine and someone said this was really expensive
Kouhai
Kouhai3mo 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
it’s raining outside
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
Kouhai3mo ago
Hmm, can you show the code where you'd need to join the lists?
it’s raining outside
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:
!τ$TAo_mickey[τ, τ]
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)); }
it’s raining outside
cant read this without code blocks
!τ$TAo_mickey[τ, τ]
ok,,i ll provide
Kouhai
Kouhai3mo ago
You could use AddRange but it'll still allocate, if you want to reduce allocations, you might wanna use ArrayPool
Anton
Anton3mo 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
it’s raining outside
i'd hope so how do i do that? how do i do that? sorry for late response
Anton
Anton3mo 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
it’s raining outside
what would this look like in code?
Anton
Anton3mo ago
look it up in the docs
Kouhai
Kouhai3mo 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
it’s raining outside
ok got it. thank you :thumbsup:
Want results from more Discord servers?
Add your server