find distinct matches in two List<string> - c#

I'm new to C# and have searched for this everywhere but could not find answer so excuse if already asked. I am trying to use a for loop to compare 2 Lists:
IList<string> List1 = new List<string> { usr.User1, usr.User2, usr.User3, usr.User4 };
IList<string> List2 = new List<string>{ "Tim", "Bob", "Brian", "Paul" };
Basically I would like there to only be 4 possible matches, so only these possible matches should count:
usr.User1 == "Tim", // e.g. User1 has to be Tim etc.
usr.User2 == "Bob",
usr.User3 == "Brian",
usr.User4 == "Paul"
I would ideally like it to return an int with a value from 0-4, so if all of the matches above were successful then it would return 4, if no matches successful then returns 0 etc.
I have tried:
int score = 0;
for (int i = 0; i <= List2.Count; i++)
{
if (List1[i] == List2[i])
{
score++;
}
}
But currently getting IndexOutOfRangeException. Many thanks.

Drop the =, you want to stop short of the upper bound.
for (int i = 0; i < List2.Count; i++)
The other option is to use zip linq:
int score = List1.Zip(List2, (a,b) => a == b ? 1 : 0).Sum();

Lose the = it should be for (int i = 0; i < List2.Count; i++).
There are probably better ways of doing this though.

Since List2 has 4 elements, List2.Count would be eqal to 4.
When you make the i <= List2.Count statement in the for loop, your are allowing the loop to indexes 0-4 because Lists are 0 index base and when the loop gets to index 4, an IndexOutOfRangeException exception would be thrown.
The solution would be to change the <= statement in the for loop to <.

Related

Check if 2 int arrays contain only unique numbers at the same index

If I have 2 int arrays a and b and they contain data like this..
a[0] = 1
a[1] = 3
a[2] = 7
b[0] = 6
b[1] = 3
b[2] = 5
How can I check if all the pairs of numbers are unique e.g. that each combination of a[i] and b[i] at the same index is not repeated in the rest of the array... So the above data would pass but if I introduced this below it would fail..
a[24] = 7
b[24] = 5
Because this combination already exists in the array at index 2. Can I do this in LINQ?
If the order of the values in the pairs over the two arrays is significant (i.e. a[0] == 1, b[0] == 2 is considered different from a[0] == 2, b[0] == 1) then one way to check for uniqueness using Linq is as follows:
bool unique = a.Zip(b).Distinct().Count() == a.Length;
If the order of the values in the pairs is NOT significant, it's slightly more fiddly:
bool unique = a.Zip(b).DistinctBy(
x => (Math.Min(x.First, x.Second), Math.Max(x.First,x.Second)))
.Count() == a.Length;
These solutions assume that missing values in one of the arrays will be ignored.
(Note: DistinctBy() is only available in .Net 6.0 or later, or via a NuGet package.)
I've managed to work this out and solve this with :
a.Zip(b, (aPos, bPos) => new { aPosition = aPos, bPosition = bPos }).Distinct().Count()
This will tell me how many distinct sets of both values at the same index and so I can work the rest out from here.
Apologies if my question wasn't clear.
try this sample :
int [] aa = a.Distinct().ToArray();
or :
public static bool HasDuplicates<T>(IList<T> items)
{
Dictionary<T, bool> map = new Dictionary<T, bool>();
for (int i = 0; i < items.Count; i++)
{
if (map.ContainsKey(items[i]))
{
return true; // has duplicates
}
map.Add(items[i], true);
}
return false; // no duplicates
}
and call :
string[] strings = new[] { "1", "2", "3" };
Utility.HasDuplicates(strings)// this will return false
int[] items=new []{1,2,3,1};
Utility.HasDuplicates(items)// this will return true

how to find out existance of specific pattern in a list of integers by help of Linq. C#

How to determine a range in a list of integer follow specific pattern.
For example, we have a list like this:
List<int> ints = new List<int>(){4,5,2,6,8,4,5,6,5,6,8,9,9};
Exists and Any could check if an element satisfies specific condition.
But what if I want to know if there is any three items in row that incremental values(plus 1): here they are {4, 5, 6}.
Patrick already answered your question with a good solution, but if you're really looking for a LINQ-only way, you could use Aggregate:
var inputs = new List<IEnumerable<int>>
{
new List<int>{ 4,5,2,6,8,4,5,6,5,6,8,9,9 },
new List<int>{ 1,2,3 },
new List<int>{ 1,2,4 },
};
foreach(var input in inputs)
{
var result = input.Aggregate(Enumerable.Empty<int>(),
(agg, cur) => agg.Count() == 3 ? agg
: agg.Any() && cur == agg.Last() + 1
? agg.Concat(new []{cur})
: new []{cur});
Console.WriteLine(result.Count() >= 3 ? String.Join(", ", result) : "not found");
}
Another way is to take all of the groups of 3 and then see which group(s) meet your n, n+1 and n+2 rule
var results = Enumerable.Range(0, ints.Count - 3)
.Select(n => ints.Skip(n).Take(3).ToArray())
.Where(three => three[0]+1 == three[1] && three[0]+2 == three[2])
.ToArray();
I would drop the LINQ requirement. It is very hard, maybe even impossible. A regular foreach statement is better suited for this:
List<int> sequence = new List<int>();
List<int> longestSequence = null;
int previous = 0;
foreach (int i in ints)
{
if (i != previous + 1 && sequence.Count > 0)
{
if (longestSequence == null || longestSequence.Count < sequence.Count)
{
longestSequence = sequence;
}
sequence = new List<int>();
}
sequence.Add(i);
previous = i;
}

For-Loop and LINQ's deferred execution don't play well together

The title suggests that i've already an idea what's going on, but i cannot explain it. I've tried to order a List<string[]> dynamically by each "column", beginning with the first and ending with the minimum Length of all arrays.
So in this sample it is 2, because the last string[] has only two elements:
List<string[]> someValues = new List<string[]>();
someValues.Add(new[] { "c", "3", "b" });
someValues.Add(new[] { "a", "1", "d" });
someValues.Add(new[] { "d", "4", "a" });
someValues.Add(new[] { "b", "2" });
Now i've tried to order all by the first and second column. I could do it statically in this way:
someValues = someValues
.OrderBy(t => t[0])
.ThenBy(t => t[1])
.ToList();
But if i don't know the number of "columns" i could use this loop(that's what I thought):
int minDim = someValues.Min(t => t.GetLength(0)); // 2
IOrderedEnumerable<string[]> orderedValues = someValues.OrderBy(t => t[0]);
for (int i = 1; i < minDim; i++)
{
orderedValues = orderedValues.ThenBy(t => t[i]);
}
someValues = orderedValues.ToList(); // IndexOutOfRangeException
But that doesn't work, it fails with an IndexOutOfRangeException at the last line. The debugger tells me that i is 2 at that time, so the for-loop condition seems to be ignored, i is already == minDim.
Why is that so? What is the correct way for this?
It's the same problem as lots of people had with foreach loops pre C# 5.
orderedValues = orderedValues.ThenBy(t => t[i]);
The value of i will not be evaluated until you call .ToList() at which point it is 2 since that's the exit condition of the for loop.
You can introduce a new local variable inside the for-loop to fix it:
for (int i = 1; i < minDim; i++)
{
var tmp = i;
orderedValues = orderedValues.ThenBy(t => t[tmp]);
}
For more information you could take a look at Eric Lippert's blog post about Closing over the loop variable considered harmful.
This is probably happening because the value of i is not closed within the loop - when the loop exits, i will have a value of 2 and then t[i] will be evaluated because of deferred execution.
One solution is to create a closing variable within the loop:
int minDim = someValues.Min(t => t.GetLength(0)); // 2
IOrderedEnumerable<string[]> orderedValues = someValues.OrderBy(t => t[0]);
for (int i = 1; i < minDim; i++)
{
var x = i;
orderedValues = orderedValues.ThenBy(t => t[x]);
}
someValues = orderedValues.ToList();

How to search for a number in a list of arrays of numbers based on the first index of each array using LINQ?

I have a list of arrays of numbers. I am searching for the two arrays where my search number falls between the numbers positioned in index 0. Then return the number positioned in index 1 from the second array. (Assume numbers in index 0 are sorted already and there are no duplicates)
My wrong solution for LINQPad:
The value of 'found' should be 3 because 9 falls between 4 & 10 in second and third array. Then I take the second found array and return 3 which is in index 1 of that array.
List<int[]> list = new List<int[]> { new[] { 1, 5 }, new[] { 4, 6 }, new[] { 10, 3} , new[] { 15, 8} };
int searchFor = 9;
int found = list.Where(n => searchFor >= n[0] && searchFor <= n[0]).Select(i => i[1]).FirstOrDefault();
found.Dump(); //should be 3 instead of 0.
Try this :
int found = list.Zip(list.Skip(1), (x, y) => x[0]<=searchFor&&y[0]>=searchFor?y[1]:0).FirstOrDefault(o=>o!=0);
Well my logic is a little different, but get the result you want. I would recommend just using a Dictionary if you are doing key-pair-value stuff like this. It makes things simpler in my opinion and if you have no repeating key's this should work fine.
// Use dictionary instead of array's if just using two int values
var dic = new Dictionary<int, int>();
dic.Add(1, 5);
dic.Add(4, 6);
dic.Add(10, 3);
dic.Add(15, 8);
int searchFor = 9;
// Don't need to find this really
int low = (from l in dic
where l.Key <= searchFor
select l.Key).Max();
// Just need this
int found = (from h in dic
where h.Key >= searchFor
select h.Value).Min();
Console.WriteLine("Low: " + low);
Console.WriteLine("Found: " + found);
How about
var found = list.First(l => l[0] > searchFor)[1];
It should do the trick as I can assume that list is ordered by each first element.
If not, then
var found = list.Orderby(l=>l[0]).First(l => l[0] > searchFor)[1];
should also work.
The expression in the where statement filters for arrays having the first element less or equal and greater or equal than 9. Since it can't be less and greater at the same time it actually filters for all arrays that have 9 as first element.
For the given data this results in an empty sequence. FirstOrDefault therefore returns the default (0 for integers).
You actually have to look for the first element greater or equal than 9:
int[] result = list.FirstOrDefault(arr => arr[0] >= searchFor);
if (result == null)
{
Console.WriteLine("Not found!");
}
else
{
Console.WriteLine(result[1]);
}

getting repeated combinations using LINQ

i have two numbers 1,2 in one array.Now i want to generate all the possible combinations of arrays(of size=5) like
1,2,0,0,0 ||
2,1,0,0,0 ||
1,0,2,0,0
.
.
.
.
.
I tried to write a recursive function but its not working
Now I want to solve this using LINQ because the code will be considerably small.
Please help me
LINQ is excellent for searching; it's much worse at generating, so it is a wrong tool for the job. So is recursion (although it is a better choice than LINQ). What you need is two nested loops, like this:
var elements = new [] {1, 2};
for (var i = 0 ; i != 5 ; i++) {
for (int j = 0 ; j != 5 ; j++) {
if (i == j) continue;
var data = new int[5];
data[i] = elements[0];
data[j] = elements[1];
ProcessCombination(data);
}
}
Here is what the code does: at each iteration of the inner loop it places the first and the second element from the elements array at distinct positions i and j. Nested loops go from 0 to 5, making sure all position combinations are covered.
You could use something like this:
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> list)
{
if (list.Count() == 1)
return new List<IEnumerable<T>> { list };
return list.Select((a, i1) => Permute(
list.Where((b, i2) => i2 != i1)).Select(
b => (new List<T> { a }).Union(b))
).SelectMany(c => c);
};
.....
var result = Permute(new int[]{1,2,0,0,0});

Categories

Resources