How linq between 2 lists by StartsWith - c#

I have two list of string.
var list1 = new List<string> { "1", "12", "21", "34", "22" };
var list2 = new List<string> { "1", "2" };
I Need select items of list1 where item StartsWith by items in list2 : "1", "12", "21", "22"
//foreach solution : "1", "12", "21", "22"
var result1 = new List<string>();
foreach (var item in list2)
result1.AddRange(list1.Where(x => x.StartsWith(item)).ToList());
//linq solution : "1"
var result2 = list1.Where(x => list2.Contains(x)).ToList();
How can I get result1 by linq solution?

You can use a combination of Where with Any like:
var query = list1.Where(s1 => list2.Any(s2 => s1.StartsWith(s2))).ToList();
and you will end up with:
{"1","12","21","22"}
another option is doing the cross join and then query like:
var query = from s1 in list1
from s2 in list2
where s1.StartsWith(s2)
select s1;

var result = list1.Where(x => list2.Any(y => x.StartsWith(y)).ToList();

Using NinjaNye.SearchExtensions you can do something like the following
using NinjaNye.SearchExtensions;
var query = list.Search(x => x).StartsWith(list2);

Related

Set subtraction while keeping duplicates

I need to get the set subtraction of two string arrays while considering duplicates.
Ex:
var a = new string[] {"1", "2", "2", "3", "4", "4"};
var b = new string[] {"2", "3"};
(a - b) => expected output => string[] {"1", "2", "4", "4"}
I already tried Enumerable.Except() which returns the unique values after subtract: { "1", "4" } which is not what I'm looking for.
Is there a straightforward way of achieving this without a custom implementation?
You can try GroupBy, and work with groups e.g.
var a = new string[] {"1", "2", "2", "3", "4", "4"};
var b = new string[] {"2", "3"};
...
var subtract = b
.GroupBy(item => item)
.ToDictionary(chunk => chunk.Key, chunk => chunk.Count());
var result = a
.GroupBy(item => item)
.Select(chunk => new {
value = chunk.Key,
count = chunk.Count() - (subtract.TryGetValue(chunk.Key, out var v) ? v : 0)
})
.Where(item => item.count > 0)
.SelectMany(item => Enumerable.Repeat(item.value, item.count));
// Let's have a look at the result
Console.Write(string.Join(", ", result));
Outcome:
1, 2, 4, 4
By leveraging the undersung Enumerable.ToLookup (which allows you to create dictionary-like structure with multi-values per key) you can do this quite efficiently. Here, because key lookups on non-existent keys in an ILookup return empty IGrouping (rather than null or an error), you can avoid a whole bunch of null-checks/TryGet...-boilerplate. Because Enumerable.Take with a negative value is equivalent to Enumerable.Take(0), we don't have to check our arithmetic either.
var aLookup = a.ToLookup(x => x);
var bLookup = b.ToLookup(x => x);
var filtered = aLookup
.SelectMany(aItem => aItem.Take(aItem.Count() - bLookup[aItem.Key].Count()));
Try the following:
var a = new string[] { "1", "2", "2", "3", "4", "4" }.ToList();
var b = new string[] { "2", "3" };
foreach (var element in b)
{
a.Remove(element);
}
Has been tested.

How do I remove the same row from a multidime list

I have two sets of data, and I put them in a multidime list as so.
List<List<string>> list1 = new List<List<string>>();
List<List<string>> list2 = new List<List<string>>();
Structure of data:
list1 = {
["John Doe", 21, "USA"],
["John Brown", 20, "Canada"]
};
list2 = {
["John Doe", 21, "USA"],
["John Mark", 22, "Brazil"]
};
What can I do to remove {"John Doe", 21, "USA"} from both lists?
EDIT:
foreach (var item in result)
{
if(i < Results.Count())
{
sourceList.Add(new List<string>());
foreach (DataColumn dtl in dtSource.Columns)
{
sourceList[i].Add(item.left[dtl.ColumnName].ToString().Trim());
}
i++;
}
else
{
targetList.Add(new List<string>());
foreach (DataColumn dtl in dtTarget.Columns)
{
targetList[j].Add(item.right[dtl.ColumnName].ToString().Trim());
}
j++;
}
}
The result variable is a union of two LINQ sets. The Result and Result2 are DataRows from the dtSource and dtTarget DataTable parameters.
public class StringListEqComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> lhs, List<string> rhs)
{
return lhs.SequenceEqual(rhs, StringComparer.Ordinal);
}
public int GetHashCode(List<string> obj)
{
// Something I cooked up real-fast. Shouldn't miss
// any matching items but also not run sequence equal
// in all the comparisons. You could improve it but
// ensure it's fast.
// Note that, the comparison operation is that first
// the hashcode of two objects are compare and if they
// match, then only the Equals method is invoked with them.
return obj.First().GetHashCode();
}
}
void Main()
{
StringListEqComparer listComp = new StringListEqComparer();
var intersect = list1.Intersect(list2, listComp);
var list1Unique = list1.Except(intersect, listComp);
var list2Unique = list2.Except(intersect, listComp);
// Just in case you need them both in a single set:
// var union = list1.Union(list2, listComp);
// var result = union.Except(intersect, listComp); // (A U B) - (A n B)
}
You can try
List<string> mylist = new List<string>(new string[] {"John Doe", 21, "USA"});
list1.Remove(mylist);
list2.Remove(mylist);
The following link might help you
https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
At first, you should find intersection elements in lists by IEnumereble.Where
var intersection= list1.Where(a => list2.Contains(a)).ToList();
Better #AviTurner advised directly to use Enumerable.Intersect
var intersect = list1.Intersect(list2).ToList();
Then remove from both lists intersection
list1.RemoveAll(i => intersect.Contains(i));
list2.RemoveAll(i => intersect.Contains(i));
Full code:
var Doe = new List<string>() { "John Doe", "21", "USA" };
List<List<string>> list1 = new List<List<string>>{
Doe,
new List<string>(){ "John Brown", "20", "Canada" },
};
List<List<string>> list2 = new List<List<string>>{
Doe,
new List<string>(){ "John Mark", "22", "Brazil" },
};
var intersect = list1.Intersect(list2).ToList().ToList();
list1.RemoveAll(i => intersect.Contains(i));
list2.RemoveAll(i => intersect.Contains(i));
.netFiddle

Find count of an list2.item in list1

I have a list1 with these items:
"Test1"
"TestB"
"TestA"
and I have list2 with these items:
"Test1"
"Test2"
"Test3"
"Test4"
"Test5"
Case: list2.Test1 is the only item from list2 which occurs in list1, thats a positive case.
if list1 has ONE item of list2 then...
How can I express that with LINQ?
var count = list2.Count(x => list1.Contains(x));
or
var count = list2.Intersect(list1).Count();
Try this
Use Except:
var count = list2.Except(list1).Count();
or
var count = list2.Intersect(list1).Count();
or
var count = list2.Count(x => list1.Contains(x));

How can I add list parameters to a LINQ query?

I have two parameters passed to the method and I need to attach them to the final query list.
(1st parameter)
string[] Price= new string[5];
Price= new string[] { "50", "25", "35" };
(2nd parameter)
List<string> DiscountPrice= new List<string>();
DiscountPrice.Add ("10");
DiscountPrice.Add ("5");
DiscountPrice.Add ("3");
var list= (from d in context.List
where ....
select new MyNewList
{
Name = d.Name,
Country = d.Country,
**Price = ??** //how do I attach the parameters one by one? In the order they were saved?
**DiscountPrice** = ??
}).ToList<MyNewList>();
It sounds like you want to match list elements by index. You can iterate from zero to the number of list elements and access each element by its index:
var prices = new string[] { "50", "25", "35" };
var discountPrices = new List<string>() { "10", "5", "3" };
var items = (from d in context.List
where ....
select new { d.Name, d.Country }).ToList();
var list = (from index in Enumerable.Range(0, items.Count())
select new MyNewList
{
Name = items[index].Name,
Country = items[index].Country,
Price = prices[index],
DiscountPrice = discountPrices[index]
}).ToList();
Another way is to Zip everything together:
var list = items.Zip(prices, (item, price) => new { item, price })
.Zip(discountPrices, (x, discountPrice) => new { x.item, x.price, discountPrice})
.Select(x => new MyNewList
{
Name = x.item.Name,
Country = x.item.Country,
Price = x.price,
DiscountPrice = x.discountPrice
})
.ToList();

Find a specific keyword of an item in a list

Say for example I have a list as follows:
List<string> myList = new List<string>();
which contains:
item1
item 2
testing 123
How could I say:
"where list item contains the value "123" return that item in full"
hence the output result would be:
testing 123
string s = myList.Where(p => p.Contains("123")).SingleOrDefault();
or
string s = myList.SingleOrDefault(p => p.Contains("123"));
Try this:
List<string> myList = new List<string>()
{
"item1",
"item 2",
"testing 123",
};
var contains123 = myList.Where(x => x.Contains("123"));
Take a look on
var list = new List<string>
{
"item1",
"item 2",
"testing 123"
};
var result = list.Find(x => x.Contains("123"));
string result = myList.Select(item => item.Contains("123")).FirstOrDefault();
if (result != null)
Console.WriteLine(result);
you can do that by using LINQ.
Example:
myList.Single(x => x == "testing 123");
if that is what you are looking for.

Categories