I'd like know if at least one element of listRef is present more than once in listA ? The other values can be present more than once.
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" };
List<string> listRef = new List<string> { "B", "D" };
Thanks,
Try this:
bool hasRef = listref.Any(r => listA.Count(a => a == r) > 1);
I would use ToLookup method to generate Lookup<string, string> first, and then use it to check your condition:
var lookup = listA.ToLookup(x => x);
return listRef.Any(x => lookup.Contains(x) && lookup[x].Count() > 1);
You could use GroupBy and ToDictionary to achieve the same:
var groups = listA.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
return listRef.Any(x => groups.ContainsKey(x) && groups[x] > 1);
something like this
var query = listRef.Where(x=>
listA.Where(a => a == x)
.Skip(1)
.Any());
listRef.ForEach(refEl => {
var count = listA.Count(aEl => aEl == refEl);
if(count > 1) {
//Do something
}
});
Finding the best performing option in this case is not simple because that depends on the number of items in the lists and the expected result.
Here's a way to do it that is performant in the face of big lists:
var appearances = listA.GroupBy(s => s)
.Where(g => g.Count() > 1)
.ToDictionary(g => g.Key, g => g.Count());
var hasItemAppearingMoreThanOnce = listRef.Any(r => appearances.ContainsKey(r));
this works
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" };
List<string> listRef = new List<string> { "A", "D" };
foreach (var item in listRef)
{
if (listA.Where(x => x.Equals(item)).Count() > 1)
{
//item is present more than once
}
}
this can be another way to do
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" , "D" };
List<string> listRef = new List<string> { "B", "D" };
var duplicates = listA.GroupBy(s => s).SelectMany(grp => grp.Skip(1));
var newData = duplicates.Select(i => i.ToString()).Intersect(listRef);
var result = listA.GroupBy(x=>x)
.Where(g=>g.Count()>1&&listRef.Contains(g.Key))
.Select(x=>x.First());
bool a = result.Any();
If the second list is large and can contain duplicates i would use a HashSet<string> and IntersectWith to remove possible duplicates and strings which are not in the first list from the second:
var refSet = new HashSet<string>(listRef);
refSet.IntersectWith(listA);
bool anyMoreThanOne = refSet.Any(rs => listA.ContainsMoreThanOnce(rs, StringComparison.OrdinalIgnoreCase));
Here the extension which is not very elegant but works:
public static bool ContainsMoreThanOnce(this IEnumerable<string> coll, String value, StringComparison comparer)
{
if (coll == null) throw new ArgumentNullException("col");
bool contains = false;
foreach (string str in coll)
{
if (String.Compare(value, str, comparer) == 0)
{
if (contains)
return true;
else
contains = true;
}
}
return false;
}
DEMO
However, if the second listRef isn't large or doesn't contain duplicates you can just use:
bool anyMoreThanOne = listRef
.Any(rs => listA.ContainsMoreThanOnce(rs, StringComparison.OrdinalIgnoreCase));
Related
I cannot find out how to filter an array/list from another list array:
I was looking for something like this:
IEnumerable<bool> Filter = new[] { true, true, false,true };
IEnumerable<string> Names = new[] { "a", "B", "c", "d" };
List<string> NameFiltered = Filter
.Where(c => c == true)
.Select(x => Names)
.ToList();
In general case (both Names and Filter are IEnumerable<T> only) I suggest Zip:
List<string> NameFiltered = Names
.Zip(Filter, (name, filter) => new {
name = name,
filter = filter, })
.Where(item => item.filter)
.Select(item => item.name)
.ToList();
If Filter is in fact an array (..Filter = new[]...) Where will do:
List<string> NameFiltered = Names
.Where((name, index) => Filter[index])
.ToList();
var NameFiltered = Enumerable.Range(0, Names.Count)
.Where(n => Filter[n])
.Select(n => Names[n])
.ToList();
There's Zip method for union of corresponding pairs of two sequences:
Filter.Zip(Names, (flag, name) => new { flag, name })
.Where(x => x.flag)
.Select(x => x.name)
Zip makes this:
IEnumerable<bool> Filter = new[] { true, true, false, true };
IEnumerable<string> Names = new[] { "a", "B", "c", "d" };
Filter.Zip(Names, (flag, name) => new { flag, name }) =
{
{ flag = true, name = "a" },
{ flag = true, name = "B" },
{ flag = false, name = "c" },
{ flag = true, name = "d" },
}
Not the best solution, but try this:
List<string> NameFiltered = Filter
.Select((x, i) => new { flag = x, index = i })
.Where(item => item.flag)
.Select(item => Names.ElementAt(item.index))
.ToList();
// Output: a, B, d
OP wants to filter Names using Filter, which is made up of bool values. If you notice that they have the same number of elements, so if filter is true. it is required to be in the resulting set. There are a couple of ways you can achieve this. I would suggest using Linq.Zip, which combines two sequences.
IEnumerable<bool> Filter = new bool[] { true, true, false, true };
IEnumerable<string> Names = new string[] { "a", "B", "c", "d" };
var merged= Filter.Zip(Names,(bo,str)=> new {bo, str});
var selected = merged.Where(x=> x.bo).Select(y=> y.str);
How about this
used Where with index
var nameFiltered = Names.Where((x, index) => Filter.ToArray()[index]);
Or
var nameFiltered= Names.Where((x, index) => Filter.ElementAt(index));
First of all there is no connection between your collections, so NameFiltered list does not know what to return.
I suggest you to make class
Names
{
}
public string Name{get;set;}
public bool Fikter{get;set;}
var NameFiltered = Names.Where(x=>x.Fikter == true).toArray;
Hi I have allLists that contains lists of string I want to find common items among these string lists
i have tried
var intersection = allLists
.Skip(1)
.Aggregate(
new HashSet<string>(allLists.First()),
(h, e) => { h.IntersectWith(e); return h);`
and also intersection ( hard code lists by index) all of them did not work when I tried
var inter = allLists[0].Intersect(allLists[1]).Intersect(allLists[2])
.Intersect(allLists[3]).ToList();
foreach ( string s in inter) Debug.WriteLine(s+"\n ");
So how am I going to do this dynamically and get common string items in the lists;
is there a way to avoid Linq?
Isn't this the easiest way?
var stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
var commonElements =
stringLists
.Aggregate((xs, ys) => xs.Intersect(ys).ToList());
I get a list with just "c" in it.
This also handles the case if elements within each list can be repeated.
I'd do it like this:
class Program
{
static void Main(string[] args)
{
List<string>[] stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
// Will contian only 'c' because it's the only common item in all three groups.
var commonItems =
stringLists
.SelectMany(list => list)
.GroupBy(item => item)
.Select(group => new { Count = group.Count(), Item = group.Key })
.Where(item => item.Count == stringLists.Length);
foreach (var item in commonItems)
{
Console.WriteLine(String.Format("Item: {0}, Count: {1}", item.Item, item.Count));
}
Console.ReadKey();
}
}
An item is a common item if it occurs in all groups hence the condition that its count must be equal to the number of groups:
.Where(item => item.Count == stringLists.Length)
EDIT:
I should have used the HashSet like in the question. For lists you can replace the SelectMany line with this one:
.SelectMany(list => list.Distinct())
I have several arrays, like:
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
... etc.
How can I get most common combination of elements in all of those arrays?
In this case it is A and B, because they occur in arr1, arr3 and arr6, and C and D, because they occur in arrays arr1, arr4 and arr5.
Just to mention that elements can be in any kind of collection, ie. in ArrayLists also.
UPDATE uuhhh, I was not clear enough...... Most common combinations of two elements in an array. That's what I tried to show in example, but did not mention in my question.
Sorry
:-((
If you are sure that each item appears only once in each array, you could just concatenate them together and get the counts, for example:
var arrs = new[] { arr1, arr2, arr3, arr4, arr5, arr6 };
var intermediate = arrs.SelectMany(a => a)
.GroupBy(x => x)
.Select(g => new { g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);
var maxCount = intermediate.First().Count;
var results = intermediate.TakeWhile(x => x.Count == maxCount);
Or if you prefer query syntax, that would be:
var arrs = new[] { arr1, arr2, arr3, arr4, arr5, arr6 };
var intermediate =
from a in arrs.SelectMany(a => a)
group a by a into g
orderby g.Count() descending
select new { g.Key, Count = g.Count() };
var maxCount = intermediate.First().Count;
var results = intermediate.TakeWhile(x => x.Count == maxCount);
The result set will contain 3 items:
Key, Count
"A", 4
"B", 4
"D", 4
Update
Given your updated question, something like this should work:
var items = arrs.SelectMany(a => a).Distinct();
var pairs =
from a in items
from b in items
where a.CompareTo(b) < 0
select new { a, b };
var results =
(from arr in arrs
from p in pairs
where arr.Contains(p.a) && arr.Contains(p.b)
group arr by p into g
orderby g.Count() descending
select g.Key)
.First();
The logic here is:
First find all distinct items in any array
Then find every pair of items to search for
Get of every pair, grouped by a list of what arrays contain that pair
Order by the groups by the number of arrays that contain each pair, descending
Return the first pair
use a Dictionary which will store an element as an index, and the occurrence count as a value. Iterate each list and count the occurrences.
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
var results = new List<IEnumerable<string>>() { arr1, arr2, arr3, arr4, arr5, arr6 }
.Select(arr => arr.Distinct())
.SelectMany(s => s)
.GroupBy(s => s)
.Select(grp => new { Text = grp.Key, Count = grp.Count() })
.OrderByDescending(t => t.Count)
.ToList();
Gives you {A, 4}, {B, 4}, {D, 4}, {C, 3}
var result = new IEnumerable<String>[] {arr1, arr2, arr3, arr4, arr5, arr6}
.SelectMany(a => a)
.GroupBy(s => s)
.GroupBy(g => g.Count())
.OrderByDescending(g => g.Key)
.FirstOrDefault()
.SelectMany(g => g.Key);
Your question is unclear as you have not clearly defined what you are looking for. In general, you could combine all the arrays into one large array and count the distinct elements. By then ordering the elements you can do whatever you intend to do with the "most common".
static void Main()
{
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
List<string> combined = Combine(arr1, arr2, arr3, arr4, arr5, arr6);
var ordered = combined.OrderBy(i => i);//sorted list will probably help other functions work more quickly such as distinct
var distinct = ordered.Distinct();
var counts = new Dictionary<string, int>();
foreach (var element in distinct)
{
var count = ordered.Count(i => i == element);
counts.Add(element, count);
}
var orderedCount = counts.OrderByDescending(c => c.Value);
foreach (var count in orderedCount)
{
Console.WriteLine("{0} : {1}", count.Key, count.Value);
}
Console.ReadLine();
}
private static List<string> Combine(string[] arr1, string[] arr2, string[] arr3, string[] arr4, string[] arr5, string[] arr6)
{
List<string> combined = new List<string>();
combined.AddRange(arr1);
combined.AddRange(arr2);
combined.AddRange(arr3);
combined.AddRange(arr4);
combined.AddRange(arr5);
combined.AddRange(arr6);
return combined;
}
Outputs: A : 4, B : 4, D : 4, C : 3
Consider three different lists of strings, one of which is a list of lists. I need to search them all to find a particular one.
In this sample, the result is achieved, but I would like to do it in one Linq statement. Note that I do not want to change the existing collections, nor create any new ones.
var collectionA = new List<string>() {"Item1", "Item2"};
var collectionB = new List<string>() { "Item3", "Item4" };
var listOfLists = new List<List<string>>() {new List<string>() {"Item5", "Item6"}, new List<string>(){ "Item7", "Item8"}};
//Is there a better Linq way to do this?
var searchString = "Item5";
var item = collectionA.FirstOrDefault(i => i == searchString);
if (item == null)
{
item = collectionB.FirstOrDefault(i => i == searchString);
if (item == null)
{
foreach (var listOfList in listOfLists)
{
item = listOfList.FirstOrDefault(i => i == searchString);
if (item != null)
{
break;
}
}
}
}
bool result = listOfLists.SelectMany(x => x)
.Concat(collectionA)
.Concat(collectionB)
.Any(x => x == "Item5");
var result = collectionA
.Concat(collectionB)
.Concat(listOfLists.SelectMany(i => i))
.FirstOrDefault(i => i == searchString);
You can use SelectMany to flatten list of list, add collectionA and collectionA to listOfLists first:
listOfLists.AddRange(new[] {collectionA, collectionB});
if (listOfLists.SelectMany(x => x).Any(y => y == "Item5"))
{
}
With you new edit which does not prefer to change the existing collections, nor create the new one, you can do:
if (listOfLists.Any(x => x.Any(y => y == "Item5"))
|| collectionA.Any(x => x == "Item5")
|| collectionB.Any(x => x == "Item5"))
{
}
Maybe this can help:
var collectionA = new List<string>() { "Item1", "Item2" };
var collectionB = new List<string>() { "Item3", "Item4" };
var listOfLists = new List<List<string>>() { new List<string>() { "Item5", "Item6" }, new List<string>() { "Item7", "Item8" } };
var val = from y in (from x in listOfLists[0] select x) where y == "Item5" select y;
you can modify more to get your expected result
I have multiple lists and i want to find distinct uncommon elements in everylist.
I came to a point but couldnt managed to get the uncommon elements but ended up common elements.
Here is the code:
private void button1_Click(object sender, EventArgs e)
{
List<DataTable> dataTables = new List<DataTable>();
List<string> c = new List<string>();
c.Add("A");
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
c.Add("A");
c.Add("B");
dataTables.Add(ss(c));
var setsOfIds = dataTables.Select(t => t.AsEnumerable().Select(x => x.Field<string>("ELIGIBLE")).OfType<string>());
var commonIds = IntersectAll<string>(setsOfIds);
var rows = dataTables.SelectMany(t => t.AsEnumerable()).Where(r => commonIds.Contains(r.Field<string>("ELIGIBLE")));
var resultRows = rows.GroupBy(r => r.Field<string>("TEDARIKCI")).Select(r => r.First());
}
private DataTable ss(List<string> aa)
{
DataTable dt = new DataTable("x");
dt.Columns.Add("ELIGIBLE", typeof(string));
DataRow dr = null;//= dt.NewRow();
foreach (string item in aa)
{
dr = dt.NewRow();
dr[0] = item;
dt.Rows.Add(dr);
}
return dt;
}
public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> hashSet = null;
foreach (var list in lists)
{
if (hashSet == null)
{
hashSet = new HashSet<T>(list);
}
else
{
hashSet.IntersectWith(list);
}
}
return hashSet == null ? new List<T>() : hashSet.ToList();
}
IntersectAll does not help me because it gives me A,B,C. But i only want B (which is common to all of each list)
I would appreciate if s.o. could give a sample. This is driving me crazy!! Pleeeeaaseeee
var l1 = new List<string> {"A", "B", "C", "D"};
var l2 = new List<string> {"B", "C", "E"};
var l3 = new List<string> {"A", "B"};
var c = l1.Intersect(l2).Intersect(l3);
returns "B"
var uc1= l1.Except(l2).Except(l3);
var uc2= l2.Except(l1).Except(l3);
var uc3= l3.Except(l1).Except(l2);
var uc=uc1.Union(uc2).Union(uc3);
should give:
uc1 = "D"
uc2 = "E"
uc3 = {empty}
uc = "D","E"
In your code, the first list had 3 elements (ABC), the second had 5(ABCBC) and the third had 7 (ABCBCAB).
In other words, all the list had A, B, C ..
Creating new List before adding to dataTable would solve this issue
List<DataTable> dataTables = new List<DataTable>();
List<string> c = new List<string>();
c.Add("A");
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
List<string> c2 = new List<string>();//New List
c2.Add("B");
c2.Add("C");
dataTables.Add(ss(c2));
List<string> c3 = new List<string>();//New List
c3.Add("A");
c3.Add("B");
dataTables.Add(ss(c3));
var setsOfIds = dataTables.Select(t => t.AsEnumerable().Select(x => x.Field<string>("ELIGIBLE")).OfType<string>());
var commonIds = IntersectAll<string>(setsOfIds);
Here commonIds list would contain only one element B
It's not entirely clear what you want. You seem to be asking for a method to find elements that only appear in one list (no 2 lists have them in common), but your expected results seem to suggest you want to find elements that ALL lists have in common. In any case, both are fairly easy with Linq.
This will find all elements that appear exactly once in all lists:
public IEnumerable<T> FindUniques<T>(IEnumerable<IEnumerable<T>> lists)
{
return lists.SelectMany(x => x).GroupBy(x => x)
.Where(g => !g.Skip(1).Any())
.Select(g => g.First());
}
This fill find all elements that appear in every list:
public IEnumerable<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
return lists.Aggregate((a, x) => a.Intersect(x));
}
For example:
var list1 = new[] { "A", "B", "C" };
var list2 = new[] { "B", "C", "D" };
var list3 = new[] { "C", "D", "E" };
FindUniques(new[] { list1, list2, list3 }); // { "A", "E" }
IntersectAll(new[] { list1, list2, list3 }); // { "C" }