Pairing two arrays - c#

Suppose I have two arrays of always equal length that could look like this:
{"A", "C", "A", "A", "B", "B", "A", "A" }
{ 1, 1, 2, 3, 1, 10, 5, 7 }
Data is paired by array index.
I wish to use LINQ to produce a result that looks like below:
A: { 1, 2, 3, 5, 7 }
B: { 1, 10 }
C: { 1 }
How can I accomplish that?
The arrays come from http request parameters. The letters applies to database names and the integers are ids in a table in the database. Next step is to connect to each database and get some data for each id.

You can first Zip them, and then group them. Use the grouped result to construct a dictionary, or whatever type you want.
string[] first = { "A", "C", "A", "A", "B", "B", "A", "A" };
int[] second = { 1, 1, 2, 3, 1, 10, 5, 7 };
var list = first.Zip(second, (f, s) => new { First = f, Second = s });
Dictionary<string, int[]> d = list.GroupBy(i => i.First)
.ToDictionary(k => k.Key, v => v.Select(val => val.Second)
.ToArray()
);

Try this
string[] s = { "A", "C", "A", "A", "B", "B", "A", "A" };
int[] t = { 1, 1, 2, 3, 1, 10, 5, 7 };
var results = s.Select((x, i) => new { s = x, t = t[i] })
.GroupBy(x => x.s).ToList();

Related

How to combine multiple lists using LINQ lambda

I have two lists:
list1 = [a,b,c,4]
list2 = [1,23,5,6]
Now I need to create an anonymous object using linq lambda.
Something like.
list1 = DataTable.AsEnumerable().toList();
list2 = DataTable.AsEnumerable().toList();
var result = list1.Where(x => x.Field<int>(1) == 2018).Select(x => new[] {
new {x = "XYZ", y = x[0], z = list2[0]},
....}
}
How do I go about doing this?
You need Zip Linq method, consider this example:
int[] list1 = {1, 2, 3};
string[] list2 = {"a", "b", "c"};
var result = list1.Zip(list2, (i, s) => new {y = i, z = i});
Your code is fine, it just needs some small fixes:
string [] list1 = { "a", "b", "c", "4" };
int[] list2 = { 1, 23, 5, 6 };
object[] list3 = { "test", DateTime.Now, 56 };
var result = list1.Where(x => x == "a").Select(x =>
new { x = "XYZ", y = x[0], z = list2[0], t = list3[1] }).ToList();

Merge, Union, Intersect C# List of Objects [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am trying to solve this problem:
I have multiple array of string with some having duplicate items. I need to come up with a final list that has most items in each list
a1 = Array{"A", "B", "C","D","E","F"};
a2 = Array{"A", "B", "B", "C","D","D","D","E","F"};
a3 = Array{"A", "B", "B", "C","D","D","E","F"};
a4 = Array{"A", "B", "B", "B", "C","D","D","E","F"};
a5 = Array{"A", "B", "B", ","D","D","E","E","F"};
Final result should be:
FinalArray = {"A", "B", "B", "B", "C","D","D","D","E","E""F"};
Max. occurrence each items accounted in the final result.
How can I achieve this?
One simple way to solve do this is to first create a list to store the results, and then iterate through the unique items in each array, and add the difference between the count of items in the current array with the count of items in the results (if it's a positive number).
For example:
var arrays = new[]
{
new[] {"A", "B", "C", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "D", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "D", "E", "F"},
new[] {"A", "B", "B", "B", "C", "D", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "E", "E", "F"},
};
var result = new List<string>();
foreach (var array in arrays)
{
var distinctItems = array.Distinct();
foreach (var distinctItem in distinctItems)
{
var diff = array.Count(i => i == distinctItem) -
result.Count(i => i == distinctItem);
if (diff > 0) result.AddRange(Enumerable.Repeat(distinctItem, diff));
}
}
Console.WriteLine(string.Join(", ", result.OrderBy(i => i)));
Output
Simple.
var arrays = new[]
{
new[] {"A", "B", "C", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "D", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "D", "E", "F"},
new[] {"A", "B", "B", "B", "C", "D", "D", "E", "F"},
new[] {"A", "B", "B", "C", "D", "E", "E", "F"},
};
var result =
arrays
.SelectMany(xs => xs.GroupBy(x => x).Select(x => new { x.Key, Count = x.Count() }))
.GroupBy(x => x.Key, x => x.Count)
.Select(x => new { x.Key, Count = x.Max() })
.SelectMany(x => Enumerable.Repeat(x.Key, x.Count))
.ToArray();
That gives: A, B, B, B, C, D, D, D, E, E, F
Sounds like a homework problem so I'm not sure if you're supposed to use specific methods/logic to solve it but I'll describe how I'd solve it.
Break down the problem. The requirement (as I understand it) is to display each value the max number of times it appears in any one set. The first step would be to count how many times each value appears in each set, which can be accomplished using ‘GroupBy’ then the ‘Count’ aggregate:
aN.GroupBy( v => v )
.Select( g => new
{
Value = g.Key,
Count = g.Count(),
} );
Similarly, we would then combine the results into a single set and group them by value to obtain the ‘Max’ Count value for use in producing the result set:
combinedResults.GroupBy( at => at.Value )
.Select( g => new
{
Value = g.Key,
Count = g.Max( at => at.Count ),
} );
Before we continue, let's combine the first two steps, but even before that, let's combine the arrays into their own set of sets.
var sets = new List<IEnumerable<string>>
{
new string[]{ "A", "B", "C", "D", "E", "F" },
new string[]{ "A", "B", "B", "C", "D", "D", "D", "E", "F" },
... etc ...
};
var valueMaxCounts = sets
.Select( s =>
s.GroupBy( v => v )
.Select( g => new
{
Value = g.Key,
Count = g.Count(),
} ) )
.GroupBy( at => at.Value )
.Select( g => new
{
Value = g.Key,
Count = g.Max( at => at.Count ),
} );
So now we have a set of Values with the max number of times each value appeared in one of the input sets. We now want to iterate through the results and add each value Count times.
var resultList = new List<string>();
foreach( var vmc in valueMaxCounts )
{
//for( var i = 0; i < vmc.Count; ++I )
//{
// resultList.Add( vmc.Value );
//}
resultList.AddRange( Enumerable.Repeat( vmc.Value, vmc.Count ) );
}
The final Select of the query and loop can be replaced with a call to SelectMany:
...query up to .GroupBy( at => at.Value )...
.SelectMany( g => Enumerable.Repeat( g.Key, g.Max( at => at.Count ) ) )

LINQ to get Distinct Count/Sort in List<List<string>>

I have a List<> that contains a List<string>, of which I need to determine the unique count from the List<string, and order by the frequency of the count.
Example:
"a","b","c"
"d","e","f"
"a","b"
"a", "b", "c"
"a", "b", "c"
"a","b"
This would output (rank / combination / frequency)
1 - "a", "b", "c" - 3
2 - "a", "b" - 2
3 "d", "e", "f" - 1
I can come up with a brute-force approach but can this be done more elegantly with LINQ? This isn't exactly a Cartesian approach from what I can tell.
Thanks.
You could write your own IEqualityComparer and use it with GroupBy.
public class StringArrayValueComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
=> x.SequenceEqual(y);
public int GetHashCode(List<string> obj)
=> obj.Aggregate(1, (current, s) => current * 31 + s.GetHashCode());
}
var list = new List<List<string>>(new[]
{
new List<string>(new [] { "a", "b", "c" }),
new List<string>(new [] { "d", "e", "f" }),
new List<string>(new [] { "a", "b" }),
new List<string>(new [] { "a", "b", "c" }),
new List<string>(new [] { "a", "b", "c" }),
new List<string>(new [] { "a", "b" })
});
var orderedList = list
.GroupBy(x => x, x => x, (x, enumerable) => new { Key = x, Count = enumerable.Count()}, new StringArrayValueComparer())
.OrderByDescending(x => x.Count)
.Select((x, index) => new { Rank = index + 1, Combination = x.Key, Frequency = x.Count });
foreach (var entry in orderedList)
{
Console.WriteLine($"{entry.Rank} - {string.Join(",", entry.Combination)} - {entry.Frequency}");
}
1 - a,b,c - 3
2 - a,b - 2
3 - d,e,f - 1

Displaying total number of common and uncommon elements between two arrays?

I have two arrays: array testAnswer holds "answers to a exam" and array inputAnswers holds "students answers to the exam".
When i run my code, it displays all the common elements of the two arrays(correct answers), and the uncommon elements (incorrect answers). However, instead of actually displaying the correct/incorrect answers, i want to be able to display the total number of correct/incorrect answers.
My code so far:
private void button1_Click(object sender, EventArgs e)
{
//Array holding answers to test
string[] testAnswer = new string[20] { "B", "D", "A", "A", "C", "A", "B", "A", "C", "D", "B", "C", "D", "A", "D", "C", "C", "B", "D", "A" };
string a = String.Join(", ", testAnswer);
//Reads text file line by line. Stores in array, each line of the file is an element in the array
string[] inputAnswer = System.IO.File.ReadAllLines(#"C:\Users\Momo\Desktop\UNI\Software tech\test.txt");
string b = String.Join(", ", inputAnswer);
//Increments through array elements in both arrays and checks for matching elements. Displays in listBox.
for (int i = 0; i < testAnswer.Length; i++)
{
if (testAnswer[i] == inputAnswer[i])
listBox1.Items.Add(inputAnswer[i]); // or testAnswer[i], as appropriate
}
//Increments through array elements in both arrays and checks for uncommon elements. Displays in listBox.
for (int i = 0; i < testAnswer.Length; i++)
{
if (testAnswer[i] != inputAnswer[i])
listBox2.Items.Add(inputAnswer[i]);
}
}
Here's how to get your results using LINQ:
var results =
testAnswer
.Zip(inputAnswer, (t, i) => new { t, i })
.Aggregate(new { Correct = 0, Incorrect = 0 },
(a, ti) => new
{
Correct = a.Correct + (ti.t == ti.i ? 1 : 0),
Incorrect = a.Incorrect + (ti.t != ti.i ? 1 : 0)
});
It'll produce an anonymous variable with this kind of result:
An alternative approach is:
var query =
testAnswer
.Zip(inputAnswer, (t, i) => t == i)
.ToLookup(x => x);
var results = new
{
Correct = query[true].Count(),
Incorrect = query[false].Count()
};
The following code will provide 2 integers at the end which will hold the answer:
private void button1_Click(object sender, EventArgs e)
{
string[] testAnswer = new string[20] { "B", "D", "A", "A", "C", "A", "B", "A", "C", "D", "B", "C", "D", "A", "D", "C", "C", "B", "D", "A" };
string a = String.Join(", ", testAnswer);
//Reads text file line by line. Stores in array, each line of the file is an element in the array
string[] inputAnswer = System.IO.File.ReadAllLines(#"C:\Users\Momo\Desktop\UNI\Software tech\test.txt");
string b = String.Join(", ", inputAnswer);
//Increments through array elements in both arrays and checks for matching elements.
//Displays in listBox.
for (int i = 0; i < testAnswer.Length; i++)
{
if (testAnswer[i] == inputAnswer[i])
listBox1.Items.Add(inputAnswer[i]); // or testAnswer[i], as appropriate
else
listBox2.Items.Add(inputAnswer[i]);
}
int correctAns = listbox1.Items.Count;
int wringAns = listbox2.Items.Count;
}
Common answers count would be Enumerable.Intersect result item count, uncommon - Enumerable.Except result item count.
Update: as long as it was mentioned in comments that it would produce wrong answers, proof that it would not:
var testAnswers = new[] { 1, 2, 3 };
var inputAnswers = new[] { 3, 2, 1 };
var commonAnswers = testAnswers
.Select((x, index) => Tuple.Create(x, index))
.Intersect(inputAnswers.Select((y, index) => Tuple.Create(y, index)));

String Array. Select

I have the following string arrays:
var array1 = new String[] { "A", "B", "C", "D" }
var array2 = new String[] { "B", "D" }
I need to do the following:
1) Find the item in array2 which appears firts in array1 (In this case is B);
2) Get the item in (1) and all the others which appear after it in array1.
So in this case I would get:
var array3 = new String[] { "B", "C", "D" }
I was trying to do it, in one step, using a lambda expression.
Is this possible?
var array3 = array1.SkipWhile(x => !array2.Contains(x)).ToArray();

Categories

Resources