Enumerating two collections - c#

class MyClass
{
string identifier;
}
I have two lists of identical count
List<MyClass> myClassList;
List<string> identifierList;
I would like to know what is the best way of assigning identifier in the myClassList enumerating over identifierList?
I wrote this, but looks too long(and inefficient?). There must be a better way of doing this?
identifierList.Select((value,index)=>new {value, index}).ToList().ForEach(x=> myClassList[x.index].identifier = x.value);

Yes, you're approach is inefficient(you're creating a throwaway list) and not very readable and also throws an exception if both lists have a different size.
You're looking for the Enumerable.Zip extension method which joins by index:
var zipped = myClassList.Zip(identifierList, (c, s) => new { class = c, string = s});
foreach(var x in zipped)
x.class.identifier = x.string;

You can use Zip:
myClassList.Zip(identifierList, (klass, id) =>
{
klass.identifier = id;
return klass
});
Note that this will give you a mutating Linq expression.
However, I suspect this is probably clearer:
for(int i = 0; i < myClassList.Count; i++)
{
myClassList[i].identifier = identifierList[i];
}
It may not be as "hip" as using Linq, but it's easier to read and understand what's going on!

Well, how about simple query like this.
myClassList.Select((e, i) => { e.identifier = identifierList[i]; return e; }).ToList();
We need this .ToList() to execute the query, otherwise it'll just do nothing.
Example :
List<string> identifierList = new List<string>()
{
"abc", "def", "ghi"
};
List<MyClass> myClassList = new List<MyClass>()
{
new MyClass(), new MyClass(), new MyClass(),
};
myClassList.Select((e, i) => { e.Id = identifierList[i]; return e; }).ToList();
foreach (var item in myClassList)
Console.Write(item.Id + " ");
Output : abc def ghi
In case your collections are of different length you can use :
myClassList.Select((e, i) => { e.Id = i < identifierList.Count? identifierList[i] : e.Id; return e; }).ToList();

Related

How to use dictionary in c# to compare two lists

Currently, I have implemented two lists with a double for loop to find matches between the two lists so I can join on them.
I have a list A which contains an ID and some other columns. I have a list B which contains an ID and some other columns. I have currently implemented a for loop within a for loop in order to make the comparisons for all the IDs so that I can find the ones that match and then return the joined results. I know want to understand how to implement a dictionary in this case as that will be more efficient to fix this problem.
public IEnumerable<Details> GetDetails(string ID)
{
// there are two lists defined up here
for (var item in listA)
{
for (var item2 in listB)
{
if (item.ID == item2.ID)
{
item.Name = item2.name;
}
}
}
return results;
}
Instead of having this double for loop, which is very inefficient. I want to learn how to implement a dictionary to fix this problem.
The dictionary would use the ids as keys (or indexes) so
Dictionary<string, object> myListA = new Dictionary<string, object>();
Dictionary<string, object> myListB = new Dictionary<string, object>();
public object GetDetails(string ID)
{
object a = myListA[ID];
object b = myListB[ID];
// combine them here how you want
// object c = a + b;
return c;
}
How about using linq to achieve your actual requirement? Something like:
public IEnumerable<A> GetDetails(int ID)
{
var listA = new List<A>
{
new A(){ ID = 1, Name = 2 },
new A(){ ID = 3, Name = 4 },
new A(){ ID = 5, Name = 6 },
};
var listB = new List<B>
{
new B(){ X = 1, name = 0 },
new B(){ X = 3, name = 1 }
};
return listA.Join(listB, k => k.ID, k => k.ID, (item, item2) =>
{
item.Name = item2.name;
return item;
}).Where(w => w.ID == ID);
}
If you just want the common IDs in the two lists, you can achieve that like this:
var commonIds = listA.Select(o => o.ID).Intersect(listB.Select(o => o.ID));

Select fails to print to console

I was hoping to use Select as a functional foreach. When I do the following, I expected it to print
foo
bar
baz
it doesn't print anything however. Howcome? The code is
List<String> strings = new List<String>(){"foo", "bar", "baz"};
strings.Select(st => { Console.WriteLine(st); return 1; });
Use ForEach:
List<String> strings = new List<String>() { "foo", "bar", "baz" };
strings.ForEach(st => { Console.WriteLine(st); });
By using Select you're basically defining anonymous functions with the following body.
Console.WriteLine(st);
return 1;
So, Console.WriteLine will only be triggered when you're iterating through the list, like this:
var x= strings.Select(st => { Console.WriteLine(st); return 1; });
foreach (var i in x){ }
or x.ToList()
And that is wrong, use ForEach :)

Is it possible to use Intersect on complex arrays in C#?

I have two arrays of student names and test scores.
Each array contains only distinct students (no duplicates) and is structured such that arrStudentGroup1[0][0] = "Bob" and arrStudentGroup1[0][1] = "98".
Given two arrays is it possible to use Intersect to create a third array of students that exists in both arrStudentGroup1 and arrStudentGroup2?
I'd like the third array to have their names and test scores. How do I do this?
If you want just the names students that are in both groups and not the associated test scores, just intersect on the name array element:
var students1 = arrStudentGroup1.Select(group => group[0]);
var students2 = arrStudentGroup2.Select(group => group[0]);
var studentsInBoth = students1.Intersect(students2);
If you also want the associated test scores you'll need to implement an IEqualityComparer<T> that compares the first element of each array.
If you want the associated test scores then join the two arrays:
var intersection = from s1 in arrStudentGroup1
join s2 in arrStudentGroup2 on s1[0] equals s2[0]
select new {Name = s1[0], Score1 = s1[1], Score2 = s2[1]}
foreach (var item in intersection)
{
Console.Writeline("{0}: s1={1}, s2={2}", Name, Score1, Score2);
}
First zip (in the general sense, rather than the Zip method) the arrays into a structure that groups students with scores:
Given:
string[][] arrStudentGroup1 = new string[][]{new string[]{"Bob","98"}, new string[]{"Alice","98"}, new string[]{"Charles","78"}, new string[]{"Dariah","99"}};
string[][] arrStudentGroup2 = new string[][]{new string[]{"Bob","98"}, new string[]{"Fiona","98"}, new string[]{"Eve","78"}, new string[]{"Dariah","99"}};
Then:
var zipped1 = arrStudentGroup1.Select(student => new {Name = student[0], Score = student[1]});
var zipped2 = arrStudentGroup2.Select(student => new {Name = student[0], Score = student[1]});
Now get the intersection. Note that if the same student name was in one but with a different score, it would not count as an intersection. That can be dealt with too, but I'm interpreting your question as not wanting that case. Let me know if I read you wrong:
var inter = zipped1.Intersect(zipped2);
Now, you can ideally work with this anyway, or even with new {Name = student[0], Score = int.Parse(student[1])} above and have a number instead of a string (more useful in most cases), which frankly is nicer than dealing with an array of arrays, along with being more typesafe. Still, if you really want it in the same format string[] format:
var interArray = inter.Select(st => new string[]{st.Name, st.Score});
And if you really, really want the whole thing in the same string[][] format:
var interArrays = interArray.ToArray();
Or for the one-line wonder (less good readability mostly, but sometimes it's nice to put a query on one line if there's other things going on in the same method):
var interArrays = arrStudentGroup1
.Select(student => new {Name = student[0], Score = student[1]})
.Intersect(
arrStudentGroup2
.Select(student => new {Name = student[0], Score = student[1]})
).Select(st => new string[]{st.Name, st.Score}).ToArray()
Output:
{"Bob", "98"},{"Dariah", "99"}
Edit: Alternatively, define an IEqualityComparer<string[]> like:
public class StudentComparer : IEqualityComparer<string[]>
{
public bool Equals(string[] x, string[] y)
{
if(ReferenceEquals(x, y))
return true;
if(x == null || y == null)
return false;
return x.SequenceEqual(y);
}
public int GetHashCode(string[] arr)
{
return arr == null ? 0 : arr.Select(s => s == null ? 0 : s.GetHashCode()).Aggregate((x, y) => x ^ y);
}
}
Then just use it directly:
var intersection = arrStudentGroup1.Intersect(arrStudentGroup2, new StudentComparer());
Gives the same output. Simpler really, but my instincts upon seeing arrays being used as objects was to get it into a real object as soon as I can, and really, it's not a bad instinct - it can make much else easier too.
Well, you could do somthing like this,
var studentsInGroup1 = arrStudentGroup1.Select(s => new
{
Name = s[0],
Score = s[1]
});
var studentsInGroup2 = arrStudentGroup2.Select(s => new
{
Name = s[0],
Score = s[1]
});
var studentsInBothGroups = studentsInGroup1.Join(
studentsInGroup2,
s => s.Name,
s => s.Name,
(one, two) => new
{
Name = one.Name,
Scores = new[] { one.Score, two.Score }
});
Which should give you a handy anonymous type you can access like this.
foreach(var student in studentsInBothGroups)
{
var Name = student.Name;
var Group1Score = student.Scores[0];
var Group2Score = student.Scores[1];
}

How do I do a group my select by a fixed number using Linq to objects?

data.Select(object=> string.Format("<a>{0}</a>", object.LinkText))
.Select(html => string.Format("<div>{0}</div>", html))
.Aggregate((running, next) => running + next);
I have this query which basically turns some objects into html-markup. What I can´t seem to achieve is that the second select should only be run for every (fixed number) 3 elements in the first select. I wan´t my ouput to be something like this:
<div><a>xxx</a><a>yyy</a><a>zzz</a></div>
<div><a>ååå</a>....</div>
Please help me avoid a for-loop!
To group by 3, use this LINQ query:
var data = new[] {"quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"};
var res = data
.Select((s, i) => new { Link = string.Format("<a>{0}</a>", s), Index = i })
.GroupBy(p => p.Index/3)
.Select(g => string.Format("<div>{0}</div>", string.Join("", g.Select(v => v.Link))));
foreach (var re in res) {
Console.WriteLine(re);
}
The output of this program looks like this:
<div><a>quick</a><a>brown</a><a>fox</a></div>
<div><a>jumps</a><a>over</a><a>the</a></div>
<div><a>lazy</a><a>dog</a></div>
Note how this code uses string.Join instead of a slower Aggregate.
Of course since you use some other objects instead of strings, you will need to replace string.Format("<a>{0}</a>", s) with string.Format("<a>{0}</a>", s.LinkText).
Add in the after the first Select .Take(3)
The following should give you the desired result:
var data = new List<String>();
data.BreakIntoChunks(3).Select(html => string.Format("<div>{0}</div>", String.Join("", (html.Select(
item => string.Format("<a>{0}</a>", item))).ToArray())));
...
public static class EnumerableExt
{
public static IEnumerable<IEnumerable<TRecord>> BreakIntoChunks<TRecord>(this IEnumerable<TRecord> items,
int chunkSize)
{
int itemCount = 0;
var processedItems = new List<TRecord>();
foreach (TRecord record in items)
{
++itemCount;
processedItems.Add(record);
if (itemCount%chunkSize == 0)
{
yield return processedItems;
processedItems.Clear();
}
}
if (processedItems.Count != 0)
{
//Because- return the items which are not multiple of chunkSize
yield return processedItems;
}
}
}
Note: The result is generated in single iteration. That's the magic of yield return!

Merging sequences by type With LINQ

I want to use LINQ to convert this
IEnumerable<int>[] value1ByType = new IEnumerable<int>[3];
value1ByType[0]= new [] { 0};
value1ByType[1]= new [] {10,11};
value1ByType[2]= new [] {20};
var value2ToType = new Dictionary<int,int> {
{100,0},
{101,1},
{102,2},
{103,1}};
to this
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{101,10},
{102,20},
{103,11}};
Is there a way to do this with LINQ? Without LINQ I would use multiple IEnumerators, one for each IEnumerable of value1ByType. like this:
// create enumerators
var value1TypeEnumerators = new List<IEnumerator<int>>();
for (int i = 0; i < value1ByType.Length; i++)
{
value1TypeEnumerators.Add(value1ByType[i].GetEnumerator());
value1TypeEnumerators[i].MoveNext();
}
// create wanted dictionary
var value2ToValue1 = new Dictionary<int, int>();
foreach (var item in Value2ToType)
{
int value1=value1TypeEnumerators[item.Value].Current;
value2ToValue1.Add(item.Key, value1);
value1TypeEnumerators[item.Value].MoveNext();
}
Any Idea how to do this in LINQ?
Not pure but you can at least do ...
var enumerators = value1ByType.Select(v => v.GetEnumerator()).ToArray();
var value2ToValue1 = value2ToType
.ToDictionary(x => x.Key, x => { enumerators[x.Value].MoveNext(); return enumerators[x.Value].Current; });
But there are so many ways this could go wrong it begs the question - why was the data in those data-structures anyway? and can you fix that instead? How did you end up with exactly the right number of references in the 2nd data structure to elements in the first?
I'm pretty sure that #Hightechrider's solution is most performant than this one, but if you really like the syntax sugar way, you can do it like this:
public IDictionary<int, int> MergeSequences(IEnumerable<int>[] value1ByType, Dictionary<int, int> value2ToType)
{
int pos = 0;
var value1ByTypePos = from byType in value1ByType
select new { Pos = pos++, Enumerator = byType.GetEnumerator() };
return (from byType in value1ByTypePos
join toType in value2ToType
on byType.Pos equals toType.Value
select new { toType.Key, Value = byType.Enumerator.GetNext() })
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
I've added an extension method to the IEnumerator interface like this:
public static T GetNext<T>(this IEnumerator<T> enumerator)
{
if (!enumerator.MoveNext())
throw new InvalidOperationException();
return enumerator.Current;
}
Now you have to be aware that any of this solutions can give you slightly different results, depending on how elements in the dictionary are enumerated. For example, another valid result to this code is:
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{103, 10},
{102, 20},
{101, 11}};
Notice that now 101 is paired with 11 and 103 is paired with 10. If this is a problem, then you should use a SortedDictionary<int, int> when defining value2ToType variable.
What you can do for sure is replace the first part with the following:
var value1TypeEnumerators = value1ByType.ToList();
instead of using an enumerator.
If I do not care about performance I could also write:
var value2Ordered = Value2ToType.OrderBy(x => x.Value).Select(x=>x.Key);
var value1Ordered = from item in value1ByType from subitem in item select subitem;
var value2ToValue1 = value2Ordered.Zip(value1Ordered, (x, y) => new { Key = x, Value = y })
.ToDictionary(item => item.Key, item => item.Value);
I used the zip method from a stackoverflow community wiki. I didn't test this with the c#4.0 zip method

Categories