Tuple to have contains and not contains list C# - c#

I have a query like this:
var foundData = await DatabaseContext.Set<TableA>()
.AsNoTracking()
.Where(x => list.Contains(x.Code))
.Select(x => x.Code)
.ToListAsync()
.ConfigureAwait(false);
var notFoundData = await DatabaseContext.Set<TableA>()
.AsNoTracking()
.Where(x => !list.Contains(x.Code))
.Select(x => x.Code)
.ToListAsync()
.ConfigureAwait(false);
As this query hits the database twice and also, I don't want to retrieve all the records from the database and then filter. Is it possible to have a List<tuple> which does it in one query like
Suppose the database contains:
A,B,C,D,E
In reality, database contains millions of records. So does the list.
and list contains A,B,F
So found list will contain: A, B
And notFound will contain: F
So expected output is var (found, notfound) = ?

Does this help you?
var source = new List<string> { "A", "B", "C", "D", "E" };
var target = new List<string> { "A", "B", "F" };
var foundOrNot = target.Select(s => new { Value = s, Found = source.Contains(s) });
foreach(var entry in foundOrNot)
{
Console.WriteLine("Value: " + entry.Value + "; Found: " + entry.Found.ToString());
}
https://dotnetfiddle.net/KL9yfK

You can do something as follow:
List<string> records = new List<string> {"A", "B", "C", "D", "E"};
List<string> list = new List<string> {"A", "B", "F"};
var result = records
.GroupBy(r => list.Contains(r))
.ToDictionary(r => r.Key);
var (found, notfound) = (result.ContainsKey(true) ? result[true].Select(g => g) : new List<string>(),
result.ContainsKey(false) ? result[false].Select(g => g) : new List<string>());

Related

Get a List of results where items != already existing items

I am developing an asp.net application and I have the following list:
public static List<SelectedSongKey_List> SelectedSongKeys(int songId)
{
var db = new BMDWorshipEntities();
var results = (from x in db.Keys
where x.SongId == songId
select x.ChordKey).ToList();
var sortingOrder = new List<string>()
{
"Ab", "A", "Bb", "B", "C", "C#", "Db", "D", "Eb", "E", "F", "F#", "Gb", "G"
};
results = results.OrderBy(x => sortingOrder.IndexOf(x)).ToList();
var skList = new List<SelectedSongKey_List>();
foreach(var sk in results)
{
skList.Add(new SelectedSongKey_List()
{
SongKey = sk.Trim()
});
}
return skList;
}
This list will populate a drop-down list with all the existing ChordKeys:
Now I would like to make another List containing all the ChordKeys (Chords are in the sortingOrder) that don't exist on the db yet.
What results statement can I write in order to do that?
You can use the Except Clause
var exceptedList = sortingOrder.Except(results).ToList();
Start with the sortingOrder and keep only items not already in results.
var notInDb = sortingOrder.Where(item => !results.Contains(item));
You could use a lambda Where statement on sortingOrder to return only those values not contained in results:
var newChords = sortingOrder.Where(c => return !results.Contains(c));
EDIT: will leave this here for posterity, but Richa Garg's answer is easier to read.

How to check if there are multiple of the same values in an array

Say I have an array which has the following values
A B A A B C
How do I run a code which will increment the integer variables a, b, and c according to the amount of the times they occur in the array
You can use GroupBy:
var array = new string[] {"A", "B", "A", "A", "B", "C" };
var counts = array
.GroupBy(letter => letter)
.Select(g => new { Letter = g.Key, Count = g.Count() });
If you want to get the counts individually, you can put everything into a dictionary
var countsDictionary = array
.GroupBy(letter => letter)
.ToDictionary(g => g.Key, g => g.Count());
var aCount = countsDictionary["A"];
var bCount = countsDictionary["B"];
//etc...
Look at the example at the bottom of https://msdn.microsoft.com/en-us/library/vstudio/bb535181%28v=vs.100%29.aspx
It basically does what you need.
var array = new string[] {"A", "B", "A", "A", "B", "C" };
int a = array.Count(p => p == "A");

Using LINQ To Interleave Two Lists

So here is what I'm attempting to do:
symbolList: 1,1,1,2,2,2
valueList: a1,b1,c1,a2,b2,c2
result: 1|a1|b1|c1,2|a2|b2|c2
The a,b,c for each symbol are all different values
For each symbol there will be the same amount of corresponding values. So for symbol 1, there are three values that I would like joined with it. For symbol 2 I want the second set of three values joined to it, etc.
However, with my current implementation, I currently get:
result: 1|a1|b1|c1|a2|b2|c2, 2|a1|b1|c1|a2|b2|c2
Here's my current code:
List<string> results = symbolList.Distinct().Select(f =>
String.Join("|", new List<string>() { f }.Concat(valueList))).ToList();
string value = string.Join(",", results);
I've attempted to use group by in some fashion, but haven't found a viable result doing that. Any help is greatly appreciated.
From your description it looks like the paired index matters, you are not trying to do a blind interleave. Here is how to do it with a GroupBy, I also put a 3 in the middle to demonstrate the ordering.
List<string> symbolList = new List<string>() { "1", "1", "3", "2", "2", "2" };
List<string> valueList = new List<string>() { "a1", "b1", "c3", "a2", "b2", "c2" };
var items = symbolList.Zip(valueList, (symbol, value) => new {symbol, value}) //pairs the like indexes with each other in to an anonymous type.
.GroupBy(pair => pair.symbol, pair => pair.value) //Group together the values that share the same symbol
.OrderBy(group => group.Key) //optional, only if you want the output to be 1,2,3
.Select(group => String.Join("|", (new[] {group.Key}).Concat(group))); //Build up the "|" separated groups.
var result = String.Join(",", items); //Join together the groupings separated by ","
Console.WriteLine("result: " + result);
Click to Run
It gives the result result: 1|a1|b1,2|a2|b2|c2,3|c3
Can you please try this out:
List<string> symbolList = new List<string>() { "1", "1", "1", "2", "2", "2" };
List<string> valueList = new List<string>() { "a", "b", "c", "a", "b", "c" };
string value = string.Join(",", symbolList.Distinct().
Select(f => String.Join("|", new List<string>() { f }
.Concat(valueList.Distinct()))).ToList());
Console.WriteLine("result: " + value);
This code gives me result: "1|a|b|c,2|a|b|c".
I hope this is what you want.
While not the answer to the original question, to strictly answer the title (which is what I came here looking for, just simply interleaving two lists):
var interleaved = list1.Zip(list2, (a, b) => new[] { a, b }).SelectMany(p => p);
Example:
var l1 = Enumerable.Range(0, 10).Select(i => (char) ('0' + i));
var l2 = Enumerable.Range(0, 10).Select(i => (char) ('A' + i));
var interleaved = l1.Zip(l2, (a, b) => new[] { a, b }).SelectMany(p => p);

C# : How to get running combination from two List<String> based on a master list

Dear all , this is something like my previous question How to get moving combination from two List<String> in C#?
I'm having a masterlist and two childlist like below
List<String> MasterList = new List<string> { "A", "B", "C", "D", "E" };
List<String> ListOne = new List<string> { "A", "B", "C" };
List<String> ListTwo = new List<String> { "B", "D" };
I just need to get the running combination from the above list for that i'm using like(previous question's answer(Thanks Danny Chen))
List<String> Result = new List<string>();
Result = ListOne.SelectMany((a, indexA) => ListTwo
.Where((b, indexB) => ListTwo
.Contains(a) ? !b.Equals(a) && indexB > indexA :
!b.Equals(a)).Select(b => string.Format("{0}-{1}", a, b))).ToList();
so the Result list will contain
"A-B"
"A-D"
"B-D"
"C-B"
"C-D"
Now my problem is the sorting issue
In the above result the fourth entry is C-B but it should be B-C. Because in the MasterList the C is after B.
How to do this in my existing linq .
Please help me to do this.
Not really clear on the exact requirement here, so does the MasterList dictate which of the two items should appear first? What about the order of the X1-X2 list? i.e. should B-C appear before B-D because C appears before D in the MasterList?
Anyway, here's something that produces the result you've asked for so far:
List<String> MasterList = new List<string> { "A", "B", "C", "D", "E" };
List<String> ListOne = new List<string> { "A", "B", "C" };
List<String> ListTwo = new List<String> { "B", "D" };
ListOne.SelectMany(i =>
ListTwo.Where(i2 => i != i2)
.Select(i2 =>
{
if (MasterList.IndexOf(i) < MasterList.IndexOf(i2))
return string.Format("{0}-{1}", i, i2);
else
return string.Format("{0}-{1}", i2, i);
}
));
outputs:
A-B
A-D
B-D
B-C
C-D

LINQ expression for shortest common prefix

Can anyone help me with a nice LINQ expression for transforming a list of strings in another list containing only the shortest distinct common prefixes for the strings? The delimiter for prefixes is ..
Example: ["A", "A.B.D", "A", "A.B","E","F.E", "F","B.C"]
Goes to: ["A", "E", "F", "B.C"]
Removed:
"A.B.D" and "A.B" because the prefix "A" is already in the list
"A" because is duplicate
"F.E" because "F" already in list
Thanks!
Here you go:
from set in
(from item in list select item.Split('.')).GroupBy(x => x[0])
select
set.First()
.TakeWhile((part, index) => set.All(x => x.Length > index && x[index].Equals(part)))
.Aggregate((x, y) => String.Format("{0}.{1}", x, y));
By way of explanation:
First, we split all the strings by '.' and group by their first token.
Then, we look at the first element of each grouping, and we take parts from it while every element of that group continues to match (TakeWhile).
Then, we take all those parts and recompose them with the Aggregate(String.Format).
var items = new[] { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C" };
var result = items
.OrderBy(s => s.Length)
.Distinct()
.ToLookup(s => s.Substring(0, 1))
.Select(g => g.First());
Order the items by their length, call distinct to remove duplicates, convert to groupings based on the first character, and select the first item in each group.
Yields:
"A", "E", "F", "B.C"
Edit: You probably don't even need Distinct as your selecting the first item in each group anyway, so it's really redundant.
EDIT: thanks to the comments for pointing out a bug in my earlier approach.
To get around that shortcoming this query should work:
var list = new List<string> { "A.B.D", "A", "A.B","E","F.E", "F","B.C", "B.C.D" };
var result = list.OrderBy(s => s)
.GroupBy(s => s[0])
.Select(g => g.First());
foreach (var s in result)
{
Console.WriteLine(s);
}
Incorrect approach:
The following query will group each string by the first character. Next, if the group count has more than one item the key is selected, otherwise the single item is selected.
var list = new List<string> { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C" };
var result = list.GroupBy(s => s[0])
.Select(g => g.Count() > 1 ? g.Key.ToString() : g.Single());
foreach (var s in result)
{
Console.WriteLine(s);
}
Nailed it - assuming that if the source list contains "Q.X" & "Q.Y" then the result should contain "Q".
var source = new []
{
"A", "A.B.D", "A",
"A.B", "E", "F.E",
"F", "B.C",
"Q.X", "Q.Y",
"D.A.A", "D.A.B",
};
Func<string, int> startsWithCount =
s => source.Where(x => x.StartsWith(s)).Count();
var results =
(from x in source.Distinct()
let xx = x.Split('.')
let splits = Enumerable
.Range(1, xx.Length)
.Select(n => String.Join(".", xx.Take(n)))
let first = startsWithCount(splits.First())
select splits
.Where(s => startsWithCount(s) == first)
.Last()
).Distinct();
// results == ["A", "E", "F", "B.C", "Q", "D.A"]
string[] source = {"A", "A.B", "A.B.D", "B.C", "B.C.D", "B.D", "E", "F", "F.E"};
var result =
source.Distinct()
.Select(str => str.Split('.'))
.GroupBy(arr => arr[0])
.Select(g =>
{
return string.Join(".",
g.Aggregate((arr1, arr2) =>
{
return arr1.TakeWhile((str, index) => index < arr2.Length
&& str.Equals(arr2[index]))
.ToArray();
}));
});
Steps:
(1) Remove duplicated elements by Distinct()
(2) Split each element to an array, also get ready to be grouped
(3) Group those arrays by the first string in the array
(4) For each group, create one common prefix by aggregating all arrays in the group. The logic for aggregating is that for two arrays arr1 and arr2, take the elements in arr1 until (1)out of bounds (2) corresponding element in arr2 is different
Note: I add two return statements in the code, to make it look cleaner. It can be shorter if remove return and its {} brackets.
How about:
var possible = new List<string> { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C" };
var shortest = possible.Distinct().Where(x => possible.Distinct().Where(y => !y.Equals(x) && x.StartsWith(y)).Count() == 0).ToList();
It checks the list against itself excluding items that are equal and any items that starts with any of the other items. I'm not sure about the effeciency though :)
I think it might be hard to solve with one single nice looking linq expression so I wrote a recursive function using linq that solves the problem:
class Program
{
static void Main(string[] args)
{
var input = new string[] { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C", "B.C.D", "B.E" };
var output = FilterFunc(input);
foreach (var str in output)
Console.WriteLine(str);
Console.ReadLine();
}
static string[] FilterFunc(string[] input)
{
if (input.Length <= 1)
return input;
else
{
var firstElem = input[0];
var indexNr = firstElem.Length;
var maxFilteredElems = 0;
for (int i = firstElem.Length; i > 0; i--)
{
var numberOfFilteredElems = input.Where(x => x.StartsWith(firstElem.Substring(0, i))).Count();
if (numberOfFilteredElems > maxFilteredElems)
{
maxFilteredElems = numberOfFilteredElems;
indexNr = i;
}
}
var prefix = firstElem.Substring(0, indexNr);
var recursiveResult = FilterFunc(input.Where(x => !x.StartsWith(prefix)).ToArray());
var result = recursiveResult.ToList();
prefix = prefix.EndsWith(".") ? prefix.Substring(0, prefix.Length - 1) : prefix;
result.Insert(0, prefix);
return result.ToArray();
}
}
}
The code could probably be more effective and more organized but don't have time for that now. I think the other solutions are wrong so far, so that's why you get my longer one. I think you need to solve it recursively to be sure to get the shortest list.
My attempt, loop through items removing anything prefixed with another item.
static void Run()
{
var list = new string[] {"A", "A.B.D", "A",
"A.B", "E", "F.E",
"F", "B.C",
"Q.X", "Q.Y",
"D.A.A", "D.A.B"
};
int size = 0;
var prefixList = new string[list.Length];
Array.Copy(list, prefixList, list.Length);
for (int i = 0; i < list.Length; i++)
prefixList
= prefixList
.Where(c => !c.StartsWith(list[i]) || c == list[i])
.Distinct()
.ToArray();
foreach (string s in prefixList)
Console.WriteLine(s);
Console.ReadLine();
}
var list = new[] { "A.B.D", "A", "E", "A.B", "F", "F.E", "B.C.D", "B.C" };
var result = from s in list
group s by s.Split('.').First() into g
select LongestCommonPrefix(g);
foreach (var s in result)
{
Console.WriteLine(s);
}
Output:
A
E
F
B.C
Method to find longest common prefix from here (replace / with .).
My understanding of the question says a list containing both "B.C" and "B.E" but no "B" would get both "B.C" and "B.E".
string[] items = { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C" };
char delimiter = '.';
var result = (from item in items.Distinct()
where !items.Any(other => item.StartsWith(other + delimiter))
select item).ToArray();
foreach (var item in result)
{
Console.WriteLine(item);
}
output
A
E
F
B.C
also works with multi-character prefixes
string[] items =
{
"Alpha",
"Alpha.Beta.Delta",
"Alpha",
"Alpha.Beta",
"Echo",
"Foxtrot.Echo",
"Foxtrot",
"Baker.Charlie"
};
gets
Alpha
Echo
Foxtrot
Baker.Charlie
If I strictly stick to the definition that dave provided, the answer is easier than it seems:
remove duplicates => distinct
remove any item that starts with any other item in the list
so we get:
from item in items.Distinct()
where !items.Any(other => other != item && item.StartsWith(other + '.'))
select item;
For the B.C and B.D question, this works as specified: Neither one includes the other, so none of the removing conditions mentioned by dave is triggered.
I admit that there might be more exciting anwers, but I'm afraid that's just not in the question ;)
Update: added delimiter to where clause in order to account for multi-char words. thanks svick!
var list = new List<string> { "A", "A.B.D", "A", "A.B", "E", "F.E", "F", "B.C" };
var result = (list.Select(a => a.Split('.').First())).Distinct();

Categories

Resources