C
C#2y ago
Alan72104

❔ LINQ distinct from 2 lists preserving order as soon as they are seen

I have 2 lists like this 1: {1, 2, 3} 2: {1, 3, 4} what's the LINQ distinct method for them that provides this output? {1, 2, 3, 4} it should be the functional equivalent as this, and not ordering by value
List<string> list = new();
for (int i = 0; i < 50; i++)
{
if (!list.Contains(lines[i])) // I know Set exists
list.Add(lines[i]);
if (!list.Contains(linesReceived[i]))
list.Add(linesReceived[i]);
}
List<string> list = new();
for (int i = 0; i < 50; i++)
{
if (!list.Contains(lines[i])) // I know Set exists
list.Add(lines[i]);
if (!list.Contains(linesReceived[i]))
list.Add(linesReceived[i]);
}
50 Replies
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
List<int> list1 = new() { 1, 2, 3 };
List<int> list2 = new() { 1, 3, 4 };
List<int> union = list1.Union(list2).ToList();

union
List<int> list1 = new() { 1, 2, 3 };
List<int> list2 = new() { 1, 3, 4 };
List<int> union = list1.Union(list2).ToList();

union
Result: List<int>
[
1,
2,
3,
4
]
[
1,
2,
3,
4
]
Compile: 525.116ms | Execution: 35.438ms | React with ❌ to remove this embed.
Alan72104
Alan72104OP2y ago
sorry I should've said doing that on a longer list fails
MODiX
MODiX2y ago
To compile C# code in Discord, use !eval Example:
!eval for(int i = 0; i < 4; i++) Console.WriteLine(i);
!eval for(int i = 0; i < 4; i++) Console.WriteLine(i);
Please don't try breaking the REPL service. Also, remember to do so in #bot-spam!
MODiX
MODiX2y ago
Alan72104#4011
REPL Result: Success
int[] a1 = { 1, 2, 3, 5, 6, 7, 8, 9 };
int[] a2 = { 1, 3, 4, 5, 7, 8, 9, 6 };
int[] names2 = a1.Union(a2).ToArray();
names2
int[] a1 = { 1, 2, 3, 5, 6, 7, 8, 9 };
int[] a2 = { 1, 3, 4, 5, 7, 8, 9, 6 };
int[] names2 = a1.Union(a2).ToArray();
names2
Result: int[]
[
1,
2,
3,
5,
6,
7,
8,
9,
4
]
[
1,
2,
3,
5,
6,
7,
8,
9,
4
]
Compile: 513.491ms | Execution: 55.842ms | React with ❌ to remove this embed.
ero
ero2y ago
hm?
Alan72104
Alan72104OP2y ago
I need the distinct values of them, starting from a1[0]/a2[0] to the last values the 4 is weirdly placed at the last index
rotor_
rotor_2y ago
Simplest possible solution would probably be to append the lists and then transform that new list into a set Perhaps chain is a thing in LINQ
Alan72104
Alan72104OP2y ago
thanks for the idea
rotor_
rotor_2y ago
Ok it's called Union it seems Oh actually The Union function actually produces a SET union So it's actually exactly what you need hahah
ero
ero2y ago
yeah but, why does that matter? you didn't specify that they need to be in order
rotor_
rotor_2y ago
Hah reading up it looks like Ero already used that exact function why am I even here lol
Alan72104
Alan72104OP2y ago
it's updating the top 50 players in the leaderboards of a game and I want to see the top 10 updated results as soon as possible because updating it has a cooldown
ero
ero2y ago
if speed is a concern, linq is the wrong choice but sure
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
List<int> list1 = new() { 1, 2, 3 };
List<int> list2 = new() { 1, 3, 4 };
List<int> orderedUnion = list1.Union(list2).Order().ToList();

orderedUnion
List<int> list1 = new() { 1, 2, 3 };
List<int> list2 = new() { 1, 3, 4 };
List<int> orderedUnion = list1.Union(list2).Order().ToList();

orderedUnion
Result: List<int>
[
1,
2,
3,
4
]
[
1,
2,
3,
4
]
Compile: 543.488ms | Execution: 38.985ms | React with ❌ to remove this embed.
ero
ero2y ago
should have used your numbers you get the idea
Alan72104
Alan72104OP2y ago
um not the order of values btw
ero
ero2y ago
then what are you asking
Alan72104
Alan72104OP2y ago
in the order of appearance
ero
ero2y ago
?
Alan72104
Alan72104OP2y ago
like in this looping method, starting to add them from index 0 Union() placed the 4 at the last index, which is false obviously bc there are still many different elements after the 4 in a2
ero
ero2y ago
it's not "false"
Alan72104
Alan72104OP2y ago
false to my requirements
ero
ero2y ago
so?
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
int[] a1 = { 1, 2, 3, 5, 6, 7, 8, 9 };
int[] a2 = { 1, 3, 4, 5, 7, 8, 9, 6 };
int[] names2 = a1.Union(a2).Order().ToArray();
names2
int[] a1 = { 1, 2, 3, 5, 6, 7, 8, 9 };
int[] a2 = { 1, 3, 4, 5, 7, 8, 9, 6 };
int[] names2 = a1.Union(a2).Order().ToArray();
names2
Result: int[]
[
1,
2,
3,
4,
5,
6,
7,
8,
9
]
[
1,
2,
3,
4,
5,
6,
7,
8,
9
]
Compile: 498.878ms | Execution: 52.171ms | React with ❌ to remove this embed.
ero
ero2y ago
what's wrong with this?
Alan72104
Alan72104OP2y ago
I did some search but still can't find a good way of doing that result should be 1 2 3 4 5 6 7 8 9
ero
ero2y ago
it is
Alan72104
Alan72104OP2y ago
well um this method is fine with the example arrays
ero
ero2y ago
show an example where it isn't fine
MODiX
MODiX2y ago
Alan72104#4011
REPL Result: Success
string[] lines1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
};
string[] lines2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae",
};

var a = lines1.Union(lines2).ToArray();
a
string[] lines1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
};
string[] lines2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae",
};

var a = lines1.Union(lines2).ToArray();
a
Result: string[]
[
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
"MinionShop",
"Calmoniidae"
]
[
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
"MinionShop",
"Calmoniidae"
]
Compile: 549.772ms | Execution: 33.969ms | React with ❌ to remove this embed.
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
string[] lines1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
};
string[] lines2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae",
};

var a = lines1.Union(lines2).Order().ToArray();
a
string[] lines1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter",
};
string[] lines2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae",
};

var a = lines1.Union(lines2).Order().ToArray();
a
Result: string[]
[
"Alan72104",
"Calmoniidae",
"KumoJustBetter",
"M4LLEY",
"MinionShop",
"varunw14",
"xXDethAngelXx"
]
[
"Alan72104",
"Calmoniidae",
"KumoJustBetter",
"M4LLEY",
"MinionShop",
"varunw14",
"xXDethAngelXx"
]
Compile: 524.397ms | Execution: 35.454ms | React with ❌ to remove this embed.
Alan72104
Alan72104OP2y ago
not ordering that by value but the order of appearance
ero
ero2y ago
what does that even mean
Alan72104
Alan72104OP2y ago
first try adding a1[0] then a2[0] then a1[1] then a2[1] I had trouble describing that to google so I came here
ero
ero2y ago
why does this matter exactly...? i don't understand...
Alan72104
Alan72104OP2y ago
if the 1st player is being ordered to the last place just because their name starts with "a" and I have 1000 players to update, each taking a 10 sec cooldown I would not be seeing the updated first page of the leaderboard in at least 10 * 1000 secs (the 1st player means the 1st place in the current leaderboard) plus there are 2 leaderboards, that's why I'm doing the distinct and ordering by appearance
Axiss
Axiss2y ago
So in your first example, MinionShop would be 3rd?
Alan72104
Alan72104OP2y ago
yes right
Axiss
Axiss2y ago
Gotcha. I don't think LINQ contains a built in method for that
Alan72104
Alan72104OP2y ago
nope, I googled for a while
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
string[] names1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter"
};

string[] names2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae"
};

string[] zip =
Enumerable.Zip(names1, names2, (n1, n2) => new[] { n1, n2 })
.SelectMany(names => names)
.Distinct()
.ToArray();

zip
string[] names1 =
{
"xXDethAngelXx",
"M4LLEY",
"Alan72104",
"varunw14",
"KumoJustBetter"
};

string[] names2 =
{
"M4LLEY",
"MinionShop",
"xXDethAngelXx",
"Alan72104",
"Calmoniidae"
};

string[] zip =
Enumerable.Zip(names1, names2, (n1, n2) => new[] { n1, n2 })
.SelectMany(names => names)
.Distinct()
.ToArray();

zip
Result: string[]
[
"xXDethAngelXx",
"M4LLEY",
"MinionShop",
"Alan72104",
"varunw14",
"KumoJustBetter",
"Calmoniidae"
]
[
"xXDethAngelXx",
"M4LLEY",
"MinionShop",
"Alan72104",
"varunw14",
"KumoJustBetter",
"Calmoniidae"
]
Compile: 635.542ms | Execution: 78.641ms | React with ❌ to remove this embed.
ero
ero2y ago
does that look right? of course, this is very very bad not performant, unnecessary allocations but it's short and works
Alan72104
Alan72104OP2y ago
ok ty I will just stay with this yep gotten
ero
ero2y ago
static class EnumerableExtensions
{
public static IEnumerable<TSource> ZipManyDistinct<TSource>(
params IEnumerable<TSource>[] collections)
{
return ZipManyDistinct(null, collections);
}

public static IEnumerable<TSource> ZipManyDistinct<TSource>(
IEqualityComparer<TSource>? comparer,
params IEnumerable<TSource>[] collections)
{
comparer ??= EqualityComparer<TSource>.Default;

IEnumerator<TSource>[] enumerators = new IEnumerator<TSource>[collections.Length];
HashSet<TSource> set = new(comparer); // set initial capacity?

try
{
for (int i = 0; i < collections.Length; i++)
{
enumerators[i] = collections[i].GetEnumerator();
}

while (true)
{
bool any = false;

for (int i = 0; i < enumerators.Length; i++)
{
IEnumerator<TSource> enumerator = enumerators[i];

if (!enumerator.MoveNext())
{
continue;
}

TSource value = enumerator.Current;

if (set.Add(value))
{
yield return value;
}

any = true;
}

if (!any)
{
yield break;
}
}
}
finally
{
for (int i = 0; i < enumerators.Length; i++)
{
enumerators[i].Dispose();
}
}
}
}
static class EnumerableExtensions
{
public static IEnumerable<TSource> ZipManyDistinct<TSource>(
params IEnumerable<TSource>[] collections)
{
return ZipManyDistinct(null, collections);
}

public static IEnumerable<TSource> ZipManyDistinct<TSource>(
IEqualityComparer<TSource>? comparer,
params IEnumerable<TSource>[] collections)
{
comparer ??= EqualityComparer<TSource>.Default;

IEnumerator<TSource>[] enumerators = new IEnumerator<TSource>[collections.Length];
HashSet<TSource> set = new(comparer); // set initial capacity?

try
{
for (int i = 0; i < collections.Length; i++)
{
enumerators[i] = collections[i].GetEnumerator();
}

while (true)
{
bool any = false;

for (int i = 0; i < enumerators.Length; i++)
{
IEnumerator<TSource> enumerator = enumerators[i];

if (!enumerator.MoveNext())
{
continue;
}

TSource value = enumerator.Current;

if (set.Add(value))
{
yield return value;
}

any = true;
}

if (!any)
{
yield break;
}
}
}
finally
{
for (int i = 0; i < enumerators.Length; i++)
{
enumerators[i].Dispose();
}
}
}
}
likely improvable
ero
ero2y ago
ero
ero2y ago
Alan72104
Alan72104OP2y ago
I will probably make it an extension too since I will keep using it for a while ty all
Anton
Anton2y ago
a.Concat(b).Distinct().OrderBy(x => x)
ero
ero2y ago
not what they want
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server