int x = 9;
List<string> list = new List<string> {"a", "b"};
I want list to be: a b a b a ... until list.Count = x. How might I achieve this?
You could do it with LINQ easily:
List<string> result = (from ignored in Enumerable.Range(0, int.MaxValue)
from item in list
select item).Take(count).ToList();
Or without using a query expression:
List<string> result = Enumerable.Range(0, int.MaxValue)
.SelectMany(ignored => list)
.Take(count)
.ToList();
The use of Enumerable.Range here is just to force repetition - like Ani's approach of using Enumerable.Repeat, which will work too of course.
How about:
var result= Enumerable.Repeat(new[] { "a", "b" }, int.MaxValue)
.SelectMany(strArray => strArray)
.Take(x)
.ToList();
Something like this should work. I did not check it, let it be an exercise for you :)
int currentCount = list.Count;
for (int i=0; i<x; ++i)
{
list.Add(list[i%currentCount]);
}
int x = 9;
List<string> list = new List<string> {};
for (int i = 0; i < x; i++)
{
list.Add("a");
list.Add("b");
}
// verify
foreach (var item in list)
{
Console.WriteLine(item);
}
Related
I have an array of lists:
var stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "d", "c" }
};
I want to extract all elements that are common in at least 2 lists. So for this example, I should get all elements ["a", "b", "c", "d"]. I know how to find elements common to all but couldn't think of any way to solve this problem.
You could use something like this:
var result = stringLists.SelectMany(l => l.Distinct())
.GroupBy(e => e)
.Where(g => g.Count() >= 2)
.Select(g => g.Key);
Just for fun some iterative solutions:
var seen = new HashSet<string>();
var current = new HashSet<string>();
var result = new HashSet<string>();
foreach (var list in stringLists)
{
foreach(var element in list)
if(current.Add(element) && !seen.Add(element))
result.Add(element);
current.Clear();
}
or:
var already_seen = new Dictionary<string, bool>();
foreach(var list in stringLists)
foreach(var element in list.Distinct())
already_seen[element] = already_seen.ContainsKey(element);
var result = already_seen.Where(kvp => kvp.Value).Select(kvp => kvp.Key);
or (inspired by Tim's answer):
int tmp;
var items = new Dictionary<string,int>();
foreach(var str in stringLists.SelectMany(l => l.Distinct()))
{
items.TryGetValue(str, out tmp);
items[str] = tmp + 1;
}
var result = items.Where(kv => kv.Value >= 2).Select(kv => kv.Key);
You could use a Dictionary<string, int>, the key is the string and the value is the count:
Dictionary<string, int> itemCounts = new Dictionary<string,int>();
for(int i = 0; i < stringLists.Length; i++)
{
List<string> list = stringLists[i];
foreach(string str in list.Distinct())
{
if(itemCounts.ContainsKey(str))
itemCounts[str] += 1;
else
itemCounts.Add(str, 1);
}
}
var result = itemCounts.Where(kv => kv.Value >= 2);
I use list.Distinct() since you only want to count occurences in different lists.
As requested, here is an extension method which you can reuse with any type:
public static IEnumerable<T> GetItemsWhichOccurAtLeastIn<T>(this IEnumerable<IEnumerable<T>> seq, int minCount, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
Dictionary<T, int> itemCounts = new Dictionary<T, int>(comparer);
foreach (IEnumerable<T> subSeq in seq)
{
foreach (T x in subSeq.Distinct(comparer))
{
if (itemCounts.ContainsKey(x))
itemCounts[x] += 1;
else
itemCounts.Add(x, 1);
}
}
foreach(var kv in itemCounts.Where(kv => kv.Value >= minCount))
yield return kv.Key;
}
Usage is simple:
string result = String.Join(",", stringLists.GetItemsWhichOccurAtLeastIn(2)); // a,b,c,d
Follow these steps:
Create a Dictionary element -> List of indices
loop over all lists
for list number i: foreach element in the list: add i to the list in the dictionary at position : dictionary[element].Add(i) (if not already present)
Count how many lists in the dictionary have two entries
You can use SelectMany to flatten the list and then pick all elemeents which occur twice or more:
var singleList = stringLists.SelectMany(p => p);
var results = singleList.Where(p => singleList.Count(q => p == q) >= 2).Distinct();
so I have a list:
["item1"]
["item2"]
["item3"]
and I want the list to be like this:
[""]
["item1"]
[""]
["item2"]
[""]
["item3"]
A simple back-to-front loop gives me just that:
for (int i = list.Count-1; i >= 0; i--)
list.Insert(i, string.Empty);
But I'm wondering if there is a more elegant way to do this with LINQ?
You could use an Intersperse extension method. That way, the meaning is clear and the code is reusable. Code taken from Extension method for Enumerable.Intersperse with slight modification to also include an empty string in the first position.
public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element)
{
foreach (T value in source)
{
yield return element;
yield return value;
}
}
Here is a way to do it:
list = list.SelectMany(x => new [] { string.Empty, x }).ToList();
But it's worth noting that this creates unnecessary arrays.If your list is big enough that might be a problem. Instead I would create a new list with a capacity and populate it using loop:
var newList = new List<string>(list.Count * 2);
int j = 0;
for(int i = 0; i < list.Count * 2; i++)
newList.Add(i % 2 == 0 ? string.Empty : list[j++]);
This will avoid resizing the list each time you add or insert items.
You can do it using SelectMany LINQ extension:
void Main()
{
List<String> items = new List<String>()
{
"1",
"2",
"3"
};
var result = items
.SelectMany(item => new String[] {"Some value", item})
.ToList();
PrintItems(result);
}
void PrintItems<T>(IEnumerable<T> items)
{
foreach(var item in items)
{
Console.WriteLine(item);
}
}
But as you understand it is not the most effective way.
Another one-liner using Aggregate:
List<string> result = list.Aggregate(new List<string>(list.Count * 2), (a, x) => { a.Add(""); a.Add(x); return a; });
I have a datatable and I am doing operations on it to take the result like this:
var result = from row in DTgraph.AsEnumerable()
group row by row.Field<string>("Campaign") into grp
select new
{
Campaign = grp.Key,
Count = grp.Count(),
SL = grp.Sum(s => s.Field<Decimal>("Inb.ServiceLevel"))
};
I want to loop on that result
I tried these two ways:
First
for (int i=0;i< result.Count(); i++){
{
}
but I couldn't type result[i].Count
second
foreach (var item in result)
{
}
Your LINQ expression returns an IEnumerable, which cannot be accessed through an indexer. This is why result[i] in your first attempt does not work.
The fix is simple: Convert your IEnumerable to a List:
var result = (from ... select new { ... }).ToList();
var result = (from row in DTgraph.AsEnumerable()
group row by row.Field<string>("Campaign") into grp
select new
{
Campaign = grp.Key,
Count = grp.Count(),
SL = grp.Sum(s => s.Field<Decimal>("Inb.ServiceLevel"))
}).ToList();
if(result.Count() > 0)
{
///Do Something////
}
Why do you need that? Use for instead.
Anyway, if you insist, you can create an extension function that will provide this functionallity. Something like this:
public static void ForEachIndex<T>(this IEnumerable<T> collection, Action<T, int> action)
{
int index = 0;
foreach (T curr in collection)
{
action(curr, index);
index++;
}
}
Usage:
List<int> collection = new List<int> {1,2,3,4,5};
int output = 0;
collection.ForEachIndex((curr,idx) => output += curr * idx);
OK, here is the problem that I am having at the moment: I want to unify same array's elements with each other.
For example: I have an array with these elements "A", "B", "C", "D"
And I want to unify them (possibly then putting them in another List) so it would become:
A,B
A,C
A,D
B,C
B,D
C,D
So far, I have tried a simple for loop but with no success and couldn't think of how to approach this further.
Here is so far what I have tried with no success:
List<string> testList = new List<string>();
List<string> anotherList = new List<string>();
testList.AddRange(richTextBox1.Text.Split(','));
anotherList.ToArray();
for (int i = 0; i < testList.Count; i++) //no idea how to get the right count
{
var union = testList[i].Union(testList[i+1]);
foreach (char value in union)
{
richTextBox2.Text = value.ToString();
}
}
Thanks.
Perhaps this simple query helps:
var query = from s1 in testList
from s2 in testList.Skip(1)
where string.Compare(s1 , s2) < 0
select string.Format("{0},{1}", s1, s2);
testList = query.ToList();
this works fine for me to match your desired output
static List<string> list = new List<string> { "A", "B", "C", "D", "E" };
static List<string> finished = new List<string>();
public static void Main()
{
for (int i = 0; i < list.Count - 1; i++)
for (int j = i+1; j < list.Count; j++)
finished.Add(list[i]+","+list[j]);
}
This might be a good start to figure out the "right count"..
http://en.wikipedia.org/wiki/Combination
Anyway...
You can do something like that (PseudoCode):
List<string> unions = new List<string>();
foreach(string s1 in list1)
{
foreach(string s2 in list2)
{
string union = s1+s2;
if(!unions.Contains(union))
unions.Add(union);
}
}
foreach(string union in unions)
Console.WriteLine(union);
You could also use some other approach using LINQ's Distinct
LINQ: Distinct values
I have list of int A,B. i like to do the following step in linq
list<int> c = new List<int>();
for (int i = 0; i < a.count; i++)
{
for (int j = 0; j < b.count; j++)
{
if (a[i] == b[j])
{
c.add(a[i]);
}
}
}
if its a and b is object , I need check particular properties like this manner and add list if it equals how can i do this in linq?
You could use the Intersect method:
var c = a.Intersect(b);
This return all values both in a and b. However, position of the item in the list isn't taken into account.
You can use Intersect:
var a = new List<int>();
var b = new List<int>();
var c = a.Intersect(b);
Produce a list c containing all elements that are present in both lists a and b:
List<int> c = a.Intersect(b).ToList();
The LINQ equivalent of your code is:
var c = from i in Enumerable.Range(0, a.Count)
from j in Enumerable.Range(0, b.Count)
where a[i] == b[j]
select a[i];
var cList = c.ToList();
But it's much nicer to do:
var c = from aItem in a
join bItem in b on aItem equals bItem
select aItem;
var cList = c.ToList();
But this doesn't filter duplicates. To filter duplicates completely, you can do:
var cList = a.Intersect(b).ToList();
If you want duplicates to show up as many times as they do in b, for example:
var aSet = new HashSet<int>(a);
var cList = b.Where(aSet.Contains)
.ToList();
This is my version of intersection:
var a = new List<int>();
var b = new List<int>();
// intersection
var c = a.Where(x => b.Any(y => x == y)).ToList();
As Chris mentions in his comment on the original question, the sample code provided will return duplicates in list c (see his comment for details). Intersect will only return distinct values. To duplicate the behavior of the original sample code, try this:
var c = (from value in a
where b.Contains(a)
select a);