I have the following code:
foreach (Logs log in _logsDutyStatusChange)
{
string driverid = log.did;
}
How would I only add distinct driver ID's to the driverid string?
Thanks
You should do:
foreach (string id in _logsDutyStatusChange.Select(x=>x.did).Distinct())
{
string driverid = id;
}
mhh ... maybe use IEnumerable< T >'s Distinct() function?
Your code isn't adding anything right now, it's just setting a string (declared in the scope of the loop) to each value. The end result would be only the last value, and it would be out of scope to the following code anyway. If you're trying to append them all to a string, separated by commas, e.g., try this:
string driverids = string.Join(",", _logsDutyStatusChange
.Select(item=>item.did)
.Distinct()
.ToArray());
This should work (without linq):
Hashset<string> alreadyEncountered = new Hashset<string>();
foreach (Logs log in _logsDutyStatusChange)
{
if(alreadyEncountered.Contains(log.did))
{
continue;
}
string driverid = log.did;
alreadyEncountered.Add(driverid);
}
First, create a comparer class and implement the IEqualityComparer<Log> interface:
public class LogComparer : IEqualityComparer<Log>
{
public bool Equals(Log one, Log two)
{
return one.did.Equals(two.did);
}
public int GetHashCode(Log log)
{
return log.did.GetHashCode();
}
}
And then use the overload of the Enumerable.Distinct() method that takes an instance of the equality comparer:
foreach(var log in _logsDutyStatusChange.Distinct(new LogComparer()))
{
// Work with each distinct log here
}
Instead of foreach'ing _logsDutyStatusChange, you could use LINQ to get a collection of distinct and loop through those, instead:
foreach (Logs log in _logsDutyStatusChange.Select(l => l.did).Distinct())
{
//Handling code here
}
Implementation will depend exactly on what you intend to do with the results.
You can use Distinct if your Logs class implements IEquatable
Otherwise, a quick 'hacky' fix can be to use something like
foreach (var group in _logsDutyStatusChange.GroupBy(l => new { log.did, .... more identifying fields })
{
string driverid = group.Key.did;
}
C# 4.0 tuples have 'automagic' comparing/equating as well
Here's one approach:
HashSet<string> SeenIDs = new HashSet<string>();
foreach (Logs log in _logsDutyStatusChange)
{
if (SeenIDs.Contains(log.did)) break;
SeenIDs.Add(log.did);
string driverid = log.did;
}
Distinct() doesn't quite work here, as it will only get you the distinct dids.
Related
C# Folks! I have 2 List that I want to compare.
Example:
List<string> ONE contains:
A
B
C
List<string> TWO contains:
B
C
I know I can achieve the results of ONE if I do:
ONE.Except(TWO);
Results: A
How can I do the same if my Lists contain a file extension for each
Element?
List<string> ONE contains:
A.pdf
B.pdf
C.pdf
List<string> TWO contains: (will always have .txt extension)
B.txt
C.txt
Results should = A.pdf
I realized that I need to display the full filename (A.pdf) in a report at the end, so I cannot strip the extension, like I originally did.
Thanks for the help!
EDIT:
This is how I went about it, but I am not sure if this is the "best" or "most performant" way to actually solve it, but it does seem to work...
foreach (string s in ONE)
{
//since I know TWO will always be .txt
string temp = Path.GetFileNameWithoutExtension(s) + ".txt";
if (TWO.Contains(temp))
{
// yes it exists, do something
}
else
{
// no it does not exist, do something
}
}
This a very straightforward and a easy code , but if your requirement has more file extension
List<string> lstA = new List<string>() { "A.pdf", "B.pdf", "C.pdf" };
List<string> lstB = new List<string>() { "B.txt", "C.txt" };
foreach (var item in lstA)
{
if (lstB.Contains(item.Replace(".pdf",".txt"))==false)
{
Console.WriteLine(item);
}
}
You can implement a custom equality comparer:
class FileNameComparer: IEqualityComparer<String>
{
public bool Equals(String b1, String b2)
{
return Path.GetFileNameWithoutExtension(b1).Equals(Path.GetFileNameWithoutExtension(b2));
}
public int GetHashCode(String a)
{
return Path.GetFileNameWithoutExtension(a).GetHashCode();
}
}
... and pass it to the Except method:
System.Console.WriteLine(string.Join(", ", list1.Except(list2, new FileNameComparer())));
So i have a main directory with sub folders and around 500k images. I know alot of theese images does not exist in my database and i want to know which ones so that i can delete them.
This is the code i have so far:
var listOfAdPictureNames = ImageDB.GetAllAdPictureNames();
var listWithFilesFromImageFolder = ImageDirSearch(adPicturesPath);
var result = listWithFilesFromImageFolder.Where(p => !listOfAdPictureNames.Any(q => p.FileName == q));
var differenceList = result.ToList();
listOfAdPictureNames is of type List<string>
here is my model that im returing from the ImageDirSearch:
public class CheckNotUsedAdImagesModel
{
public List<ImageDirModel> ListWithUnusedAdImages { get; set; }
}
public class ImageDirModel
{
public string FileName { get; set; }
public string Path { get; set; }
}
and here is the recursive method to get all images from my folder.
private List<ImageDirModel> ImageDirSearch(string path)
{
string adPicturesPath = ConfigurationManager.AppSettings["AdPicturesPath"];
List<ImageDirModel> files = new List<ImageDirModel>();
try
{
foreach (string f in Directory.GetFiles(path))
{
var model = new ImageDirModel();
model.Path = f.ToLower();
model.FileName = Path.GetFileName(f.ToLower());
files.Add(model);
}
foreach (string d in Directory.GetDirectories(path))
{
files.AddRange(ImageDirSearch(d));
}
}
catch (System.Exception excpt)
{
throw new Exception(excpt.Message);
}
return files;
}
The problem I have is that this row:
var result = listWithFilesFromImageFolder.Where(p => !listOfAdPictureNames.Any(q => p.FileName == q));
takes over an hour to complete. I want to know if there is a better way to check in my images folder if there are images there that doesn't exist in my database.
Here is the method that get all the image names from my database layer:
public static List<string> GetAllAdPictureNames()
{
List<string> ListWithAllAdFileNames = new List<string>();
using (var db = new DatabaseLayer.DBEntities())
{
ListWithAllAdFileNames = db.ad_pictures.Select(b => b.filename.ToLower()).ToList();
}
if (ListWithAllAdFileNames.Count < 1)
return new List<string>();
return ListWithAllAdFileNames;
}
Perhaps Except is what you're looking for. Something like this:
var filesInFolderNotInDb = listWithFilesFromImageFolder.Select(p => p.FileName).Except(listOfAdPictureNames).ToList();
Should give you the files that exist in the folder but not in the database.
Instead of the search being repeated on each of these lists its optimal to sort second list "listOfAdPictureNames" (Use any of n*log(n) sorts). Then checking for existence by binary search will be the most efficient all other techniques including the current one are exponential in order.
As I said in my comment, you seem to have recreated the FileInfo class, you don't need to do this, so your ImageDirSearch can become the following
private IEnumerable<string> ImageDirSearch(string path)
{
return Directory.EnumerateFiles(path, "*.jpg", SearchOption.TopDirectoryOnly);
}
There doesn't seem to be much gained by returning the whole file info where you only need the file name, and also this only finds jpgs, but this can be changed..
The ToLower calls are quite expensive and a bit pointless, so is the to list when you are planning on querying again so you can get rid of that and return an IEnumerable again, (this is in the GetAllAdPictureNames method)
Then your comparison can use equals and ignore case.
!listOfAdPictureNames.Any(q => p.Equals(q, StringComparison.InvariantCultureIgnoreCase));
One more thing that will probably help is removing items from the list of file names as they are found, this should make the searching of the list quicker every time one is removed since there is less to iterate through.
I have this bit of code in a class:
public class TicketSummary
{
//get all the development tickets
public List<IncidentSummary> AllDevelopmentTickets { get; set; }
public List<string> TicketNames()
{
List<string> v = new List<string>();
foreach (var developmentTicket in AllDevelopmentTickets)
{
var ticketIds = developmentTicket.id.ToString(CultureInfo.InvariantCulture);
v.Add(ticketIds);
}
return v;
}
}
}
And I am trying to see if my API connection (plus all the code) did it's job and pulls back the tickets and their info, more specifically the ids.
In my main program I have no clue how to check if it did the job. I tried something but it isn't quite right and doesn't return anything ( I know I need a Console.WriteLine)
static void Main(string[] args)
{
Console.ReadLine();
var tickets = new TicketSummary();
tickets.TicketNames();
while ( tickets != null )
{
Console.WriteLine(tickets);
}
}
Any suggestions, please?
Thank you!
You've dropped the returned result: tickets.TicketNames(); returns List<String> that you have to assign and then itterate:
var tickets = new TicketSummary();
var names = tickets.TicketNames(); // <- names, List<String> according to the code
// printing out all the names
foreach(var name in names)
Console.WriteLine(name);
Do you mean you just want to print all the tickets out?
foreach (var ticket in tickets.TicketNames())
{
Console.WriteLine(ticket);
}
You have several problems in your code, that should keep it from even compiling, but aside from that, It seem's what you're really after is rather transforming the data in AllDevelopmentTickets, rather than moving it somewhere. So you could probably do it with a Select call (from LINQ). So, in your main method:
var tickets = new TicketSummary();
// add some tickets to tickets.AllDevelopmentTickets here...
var ticketNames = tickets.AllDevelopmentTickets.Select(ticket => ticket.id.ToString();
// Yes, you should probably use an UI culture in the ToString call.
// I'm just trying to limit my line width =)
Now, ticketNames should be an IEnumerable<string> holding all the ticket ids. To, for example, print them out, you can iterate over them and write to console output:
foreach (var name in ticketNames) {
Console.WriteLine(name);
}
You need to assign / use the return value of the TicketNames() method. This seems a lot of work just to return the string version of the TicketId. This can be reduced
public List<string> TicketNames()
{
return AllDevelopmentTickets
.Select(t => t.id.ToString(CultureInfo.InvariantCulture))
.ToList();
}
var ticketSummary = new TicketSummary();
var ticketNames = ticketSummary.TicketNames();
foreach(var ticketName in ticketNames)
{
Console.WriteLine(ticketName);
}
or Even just:
foreach(var ticketName in AllDevelopmentTickets
.Select(t => t.id.ToString(CultureInfo.InvariantCulture)))
{
Console.WriteLine(ticketName);
}
You're ignoring the returned value.
static void Main(string[] args)
{
Console.ReadLine();
var tickets = new TicketSummary();
var res = tickets.TicketNames();
while ( for r in res )
{
Console.WriteLine(r);
}
}
I want the query to give me back a NotificationConfiguration and also an IEnumerable<string> (which I will later transform to a single string using SB). Once the query gives me back those two items I will transform it using a constructor in my view model so I can properly display all the data using a DataTable. The answer I am looking for maybe very specific but I need to understand why I'm getting the subquery error when I want it to return an IEnumerable<string> and how to fix it. Also please note... according to the .net docs the code should be handing my back an IEnumerable<string> but for some reason its still crashing. Here is the relevant code samples again:
[HttpPost]
public ActionResult Index(DataTableRequest requestedData)
{
using (this.dataStore.Session.BeginTransaction())
{
return this.dataStore.Query<NotificationConfiguration>()
.TableRange(requestedData, p => new NotificationConfigurationViewModel(p, p.Events.Select(x => x.Code) ?? null));
}
}
.
public NotificationConfigurationViewModel(NotificationConfiguration notification , IEnumerable<string> events)
{
Contract.Requires(notification != null);
this.notification = notification;
this.events = events;
}
.
[Display(Name = "Events")]
public virtual string EventTypeCodes
{
get
{
var codes = new StringBuilder();
foreach (var item in this.events)
{
codes.Append(item + ",");
}
return codes.ToString().TrimEnd(',');
}
}
This might have been dealt with, so I apologize in advance for that.
Anyway, this is my somewhat contrived example, i hope it gets my question across:
Say we have these classes
class WordExamples
{
public string word;
public List<Sentence> sentencesWithWord;
//constructor
public WordExamples(string word) {
this.word = word;
}
}
class Sentence
{
public List<string> words;
}
Then we set up two lists:
List<Sentence> sentences = GetSomeSentences();
List<WordExamples> wordExamples =
GetSomeWords().Select(w=>new WordExamples(w));
As you can see the list of WordExamples contains word examples that are incomplete in that they don't have the sentencesWithWord List instantiated.
So what I need is some neat Linq that will set this up. I.e. something like:
foreach wordExample get the subset of sentences that contain the word and assign it to sentencesWithWord. (Withouth nested for loops that is)
edit:
Adding public access modifier
It's not really clear what you're after, but I suspect you want:
foreach (var example in wordExamples)
{
Console.WriteLine("Word {0}", example.Key);
foreach (var sentence in example)
{
// I assume you've really got the full sentence here...
Console.WriteLine(" {0}", string.Join(" ", sentence.Words));
}
}
EDIT: If you really need the WordExamples class, you could have:
public class WordExamples
{
public string Word { get; private set; }
public List<Sentence> SentencesWithWord { get; private set; }
public WordExamples(string word, List<Sentences> sentences) {
Word = word;
// TODO: Consider cloning instead
SentencesWithWord = sentences;
}
}
This is basically just like an element of a Lookup, mind you...
Anyway, with that in place you could use:
var wordExamples = from sentence in sentences
from word in sentence.Words
group sentence by word into g
select new WordExample(g.Key, g.ToList());
As LINQ is a query language, not an assignment language, you should use a loop:
List<WordExamples> wordExamples = GetSomeWords().Select(w=>new WordExamples(w))
.ToList();
foreach(var wordExample in wordExamples)
{
wordExample.sentencesWithWord
= sentences.Where(x => x.words.Contains(wordExample.word)).ToList();
}
IEnumerable<WordExamples> wordExamples = GetSomeWords().Select(w=>
{
var examples = new WordExamples(w);
examples.sentencesWithWord = sentences.Where(s => s.words.Any(sw => sw == w)).ToList();
return examples;
}
);
Don't forget to set correct access modifiers.
It looks like you're re-inventing an ILookup.
ILookup<string, Sentence> examples = GetSentences()
.SelectMany(sentence => sentence.words, (sentence, word) => new {sentence, word} )
.ToLookup(x => x.word, x => x.sentence);