I am trying to convert the Linq var to List.my c# code is
private List<HyperlinkInfo> GetHyperlinkByCode()
{
TourInfoBusiness obj = new TourInfoBusiness();
List<HyperlinkInfo> lst = new List<HyperlinkInfo>();
lst = obj.GetAllHyperlink();
//lst = lst.Select(x => x.Attraction).ToList();
var k = lst.Select(x => x.Attraction).Distinct();
}
if you look at the above code till the Line var k = lst.Select(x => x.Attraction).Distinct(); is ok Now can i convert var k to List.
According to your comments you need single HyperlinInfo object for each Attraction value (which is string). So, use grouping and ToList():
private List<HyperlinkInfo> GetHyperlinkByCode()
{
TourInfoBusiness obj = new TourInfoBusiness();
List<HyperlinkInfo> lst = obj.GetAllHyperlink();
return lst.GroupBy(x => x.Attraction) // group links by attraction
.Select(g => g.First()) // select first link from each group
.ToList(); // convert result to list
}
Also you can use morelinq DistinctBy extension (available from NuGet):
private List<HyperlinkInfo> GetHyperlinkByCode()
{
TourInfoBusiness obj = new TourInfoBusiness();
List<HyperlinkInfo> lst = obj.GetAllHyperlink();
return lst.DistinctBy(x => x.Attraction).ToList();
}
Use Enumerable.ToList<TSource> Method. Just Add ToList() at the end of your query or
return k.ToList();
So your method can be:
private List<HyperlinkInfo> GetHyperlinkByCode()
{
TourInfoBusiness obj = new TourInfoBusiness();
List<HyperlinkInfo> lst = new List<HyperlinkInfo>();
lst = obj.GetAllHyperlink();
//lst = lst.Select(x => x.Attraction).ToList();
var k = lst.Select(x => x.Attraction).Distinct();
return k.ToList();
}
But x.Attraction should be HyperLinkInfo type object.
EDIT: Based on comment it appears that x.Attraction is a string, you need to create object of your class Project.Bll.HyperlinkInfo in select statement and then return that list. Something like:
var k = lst.Select(new Project.Bll.HyperLinkInfo(x => x.Attraction)).Distinct();
Assuming that Project.Bll.HyperlinkInfo constructor takes a string parameter to return a HyperLinkInfo object.
Use this:
var k = lst.Select(x => x.Attraction).Distinct().ToList();
Now k is List of x.Attraction type. If your x.Attraction is string, use this:
List<string> k = lst.Select(x => x.Attraction).Distinct().ToList();
Use ToList() to your query;
Creates a List<T> from an IEnumerable<T>.
List<HyperlinkInfo> k = lst.Select(x => x.Attraction).Distinct().ToList();
try this add DistinctBy of moreLinq:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
and call it in your code:
lst.DistinctBy(x => x.Attraction).toList();
Try this code:
return (List<Hyperlink Info>) k
Related
I am trying to find the nearest integer to a set integer from an array but want to pick a random one if there is more than one.
var nearestScore = scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).First();
I don't know any other way but using First().
I have also tried
var r = new random()
var nearestScore = scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).ToArray();
return nearestScore[r.next(0, nearestScore.Length - 1)]
Thanks.
You could use GroupBy() for this. It's a little heavy-handed but simple for small arrays.
Random rnd = new Random();
var nearestScore = scoreArray
.Select(x => new { Num = x, Delta = Math.Abs((long)x - realScore)) })
.OrderBy(a => a.Delta)
.GroupBy(a => a.Delta)
.First()
.OrderBy(a => rnd.Next())
.Select(a => a.Num)
.First();
Brake it in few statements:
var minScore = scoreArray.Min(x => Math.Abs((long)x - realScore));
var list = scoreArray.Where(x => Math.Abs((long)x - realScore) == minScore).ToList();
// or you can use GroupBy with OrderBy and First instead of above, but this is lighter and cleaner
r = new Random();
var myRandomNumber = r.Next(0, list.Count);
return list[myRandomNumber];
You can add an Extension FirstEqual:
public static IEnumerable<T> FirstEqual<T>(this IEnumerable<T> source)
{
var e = source.GetEnumerator();
if (e.MoveNext())
{
T first = e.Current;
yield return first;
while (e.MoveNext())
{
if (e.Current.Equals(first))
yield return first;
else
break;
}
}
}
and than you call
scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).FirstEqual()
This will return you a collection of equal numbers.
We could also return a Tuple of the first number and its Count.
I have a List looks like:
List<int> List1= new List<int>(){3,4,5};
and another looks like:
List<int> List2 = new List<int>(){1,2,3,4,5,6};
How can I use Linq to get an array of all of the indices of List1 from List2 like below:
var ResultList = {2,3,4};
var ResultList = List1.Select(x => List2.IndexOf(x));
This is a longer solution but prevents a nested loop through the array which may be faster if the arrays are huge (but slower if the arrays are small).
List<int> List1= new List<int>(){3,4,5};
List<int> List2 = new List<int>(){1,2,3,4,5,6};
var lookup = new Dictionary<int, int>();
for(var i=0; i<List2.Count; i++) {
lookup[List2[i]] = i;
}
List<int> Result = List1.Select(i => {
int index;
return lookup.TryGetValue(i, out index) ? index : -1;
}).ToList();
You can also do the overloaded version of Select statement to select the Value and return the Index:
var result = List2.Select((a, b) => new {Value = a, Index = b})
.Where(x => List1.Any(d => d == x.Value))
.Select(c => c.Index).ToArray();
If your List2 contains more than one instance of a List1 value (or Equality) type, then you can use the indexed overload of Select to find all the duplicates:
var List1= new List<int>(){3,4,5};
var List2 = new List<int>(){1,2,3,4,5,6,1,2,3,5};
var result = List2.Select((x, idx) => Tuple.Create(x, idx))
.Where(t => List1.Contains(t.Item1))
.Select(x => x.Item2)
// 2,3,4,8,9
or better, using C#7 Value Tuples
List2.Select((x, idx) => (X:x, Idx:idx))
.Where(t => List1.Contains(t.X))
.Select(x => x.Idx);
(.IndexOf returns just the first index found in the target)
We are working on some LINQ stuff and are new to using the GroupBy extension.
I am editing this post to include my actual code as I tried to use some simple example but it seems that it making it more confusing for those trying to help. Sorry for that.
NOTE We need to sum the Amount field below. We did not attempt that yet as we are just trying to figure out how to extract the list from the groupBy.
Here is my code:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID).GroupBy(g => g.termLength, o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
}).Select(grp => grp.ToList()));
The error we get when trying to do this is:
Argument 1: cannot convert from
IEnumerable<List<MyCSTotal2>> to IEnumerable<MyCSTotal2>
EDIT: Thanks for the help. Here is what we ended up with:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID)
.GroupBy(g => g.termLength)
.SelectMany(cl => cl.Select( o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = cl.Sum(m=>m.RMR1YrTotal / 12),
RuleEvaluation = 0
})));
In order to flatten the groups you need to use SelectMany extension method:
SelectMany(grp => grp.ToList())
But if that is your current query you don't need to group, you need to project your collection using Select:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID)
.Select( o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
});
I see no reason in using GroupBy as there are no aggregation functions involved. If you want to have Persons distinct by termLength. Write a DistinctBy. You will get the desired collection this way
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Then use the extension like this
var collection = userTotals
.Where(w => w.priceinfoId == priceinfoID)
.DistinctBy(g => g.termLength)
.Select(o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
});
I have dictionary object like this:
var dictionary = new Dictionary<string, List<int>()>;
The number of keys is not very large but the list of integers in the value can be quite large (in the order of 1000's)
Given a list of keys (keylist), I need to count the number of times each integer appears for each key and return them ordered by frequency.
Output:
{int1, count1}
{int2, count2}
...
This is the solution I have come up with:
var query = _keylist.SelectMany(
n=>_dictionary[n]).Group(g=>g).Select(
g=> new[] {g.key, g.count}).OrderByDescending(g=>g[1]);
Even when this query produces the desired result, it's not very efficient.
Is there a clever way to produce the same result with less processing?
I would do it this way:
var query =
from k in _keylist
from v in dictionary[k]
group v by v into gvs
let result = new
{
key = gvs.Key,
count = gvs.Count(),
}
orderby result.count descending
select result;
To me this is quite straight forward and simple and well worth accepting any (minor) performance hit by using LINQ.
And alternative approach that doesn't create the large list of groups would be to do this:
var query =
_keylist
.SelectMany(k => dictionary[k])
.Aggregate(
new Dictionary<int, int>(),
(d, v) =>
{
if (d.ContainsKey(v))
{
d[v] += 1;
}
else
{
d[v] = 1;
}
return d;
})
.OrderByDescending(kvp => kvp.Value)
.Select(kvp => new
{
key = kvp.Key,
count = kvp.Value,
});
From an algorithmic space- and time-usage point of view, the only thing I see that is suboptimal is the use of GroupBy when you don't actually need the groups (only the group counts). You can use the following extension method instead.
public static Dictionary<K, int> CountBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector)
{
return source.SumBy(keySelector, item => 1);
}
public static Dictionary<K, int> SumBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, int> valueSelector)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (keySelector == null)
{
throw new ArgumentNullException("keySelector");
}
var dictionary = new Dictionary<K, int>();
foreach (var item in source)
{
var key = keySelector(item);
int count;
if (!dictionary.TryGetValue(key, out count))
{
count = 0;
}
dictionary[key] = count + valueSelector(item);
}
return dictionary;
}
Note the advantage is that the lists of numbers are enumerated but not stored. Only the counts are stored. Note also that the keySelector parameter is not even necessary in your case and I only included it to make the extension method slightly more general.
The usage is then as follows.
var query = _keylist
.Select(k => _dictionary[k])
.CountBy(n => n)
.OrderByDescending(p => p.Value);
This will you get you a sequence of KeyValuePair<int, int> where the Key is the number from your original lists and the Value is the count.
To more efficiently handle a sequence of queries, you can preprocess your data.
Dictionary<string, Dictionary<int, int>> preprocessedDictionary
= _dictionary.ToDictionary(p => p.Key, p => p.Value.CountBy(n => n));
Now you can perform a query more efficiently.
var query = _keylist
.SelectMany(k => preprocessedDictionary[k])
.SumBy(p => p.Key, p => p.Value)
.OrderByDescending(p => p.Value);
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