15 ms·
LINQ Ruined My Favorite Interview Question
- T-zex 13y agoThe interviewer doesn't see the difference between the LINQ and extension methods and he hasn't provided a single line of LINQ in his post.
- jmcqk6 13y agoYou're confusing the syntax with the functionality.
- joshuaellinger 13y agoI am a big fan of LINQ but it gives and takes away complexity at the same time. It makes a 'whole class of things that you would have to do with loops' go away. Once you are comfortable with the syntax, it makes code a lot more readable. But you lose track of when and where things are getting executed. For example, it is easy to make something that you intend to execute inside of SQL server run inside of C# code. And then, all of a sudden, string comparison is case-sensitive. You wind up having to context switch between procedural and set mentality without the same kind of visual cues you used to get.
- jmcqk6 13y agoI've ran into this a little bit as well, but I feel the benefits far outweigh the disadvantages. Once you understand the basics, your code becomes so much cleaner. You do have to be careful for hidden pitfalls, like an accidental N+1 select against the database, but even that is usually fixed pretty easily if you simple call .Include() in the original expression. From the article, I really don't understand the concept of not updating your knowledge of Linq. It's been out for years now. And then the first concern is with performance? That sounds very problematic to me. There are a lot of .NET developers out there that are still stuck in the previous decade. There are a lot of shops that are still on 2.5, which is too bad, because that was when the framework and C# really started to take off.
- ryanmolden 13y agoThere is also some trickiness around when things get executed. If you return an IEnumerable<T> that is really a LINQ query, and the caller doesn't immediately enumerate it, they run the risk of getting exceptions when they do enumerate if you mutate the collection the query runs over. You can of course force its evaluation, but that also loses some of the benefits of the laziness. Bring in multi-threading and even if you properly synchronize collection access (and that is hard to do without custom collection types, since LINQ is like the honey badger w.r.t. synchronized access) you can still see the collection modified exceptions talked about above.
- kryten 13y agoIt's also really easy to shoot yourself with unless you understand everything and think each problem through. Four killer issues I've seen so far: We had a major production performance issue which turned out to be a stray ToList which was causing a massive memory ballooning. Didn't get noticed in test as the test cases passed but it hit prod and 2000 users bashed it and tried to allocate 20Mb each causing our cluster to shit a brick. Null reference exceptions! There are so many dereferencing operations in an average LINQ expression that you really have no idea which one is blowing if it goes pop in production in release config. People using .Single(...) and getting more or less than one result back. So frustrating. If you push an IEnumerable<T> over an interface boundary the performance and memory semantics are not preserved and you end up with a leaky abstraction. These are shits to resolve. Example: queries executing inside the view which is outside the transaction scope. We've had to ban it in some circumstances.
- usea 13y agoI agree with your pain points. We try to use ICollection in our APIs instead of IEnumerable, since the latter can have surprising semantics like being a wrapper for some operation which may not be valid anymore, or might be slower than you expect to do things like .Count(). IMO it's really not best for transporting across interface boundries in the most common case; only when you're specifically trying to avoid having the whole collection in memory or something like that. Another thing that can help is this wonderful May<T> library[1]. It a great option type[2] for .NET. It helps make operations more composable. [1] https://github.com/Strilanc/May https://github.com/Strilanc/May [2] http://en.wikipedia.org/wiki/Option_type http://en.wikipedia.org/wiki/Option_type
- venomsnake 13y agoBut you lose track of when and where things are getting executed. That is the curse of modern software development. But LINQ is minor offender here compared to some kinds of remoting, code reflects from endless xml configs wrapped in gazillion interfaces and some inventive orm-s. Sadly our tools have not kept up with the complexity that is external to the executing code. The debugger in the pre- inversion of control/bean injection days was almighty because you had all of the program state in front of you. A little known fact is that if you wrap Java/C# code in enough unit tests you get Haskell.
- joshuaellinger 13y ago> A little known fact is that if you wrap Java/C# code in enough unit tests you get Haskell. It is enough to make you want to switch to a pure message passing system, isn't it?
- MichaelGG 13y agoThe "execute inside SQL but ends up in C#" is because of the terrible decision of the C# language designers to make reified code (expression trees) have the exact same syntax as lambda functions that are normal code. If you had to tell the compile if you wanted code or a tree, that problem would be solved. It'd also be one step closer to allowing type inference for lambdas assigned to locals.
- deleted 13y ago[deleted]
- overgard 13y agoLINQ tends to get thought of as "database syntax sugar", but it's way more than that. It's C#'s version of the lazy collection operations you find in most functional languages, just given friendlier sqlish names. (IE, "Select" instead of "map" and "Where" instead of "filter") I almost feel a bit gross when I have to write a "foreach" loop at this point, because there's almost always an equivalent way to do it in LINQ (although it's a tradeoff, as the one downside of LINQ is that given it's deferred nature, it's harder to debug).
- chiph 13y agoI do Lazy LINQ, by which I mean I write the looping code with "foreach"es and get it debugged & working, but then allow Resharper to convert it to LINQ if it can. I think this is mostly because I'm still not comfortable with the syntax, and partly because of the set/iterative impedance mismatch that is there. "It's not you, LINQ. It's me."
- weavie 13y agoYou would really benefit from learning a functional language (any) to help you roll your own LINQ. Some of Resharpers refactoring to LINQ is awful and does not make for cleaner code.
- untog 13y agoI almost feel a bit gross when I have to write a "foreach" loop at this point Agreed. I've transitioned to spending most of my time in JS, and wherever I can I use .map(), but the chaining it's not quite the same as LINQ. Someday I intend to write a library of Array addons to provide GroupBy and so on, but I can't imagine it'll be super efficient.
- alevans4 13y agoUnderscore has groupBy and some of the others you may be looking for.
- 13y ago
- azurelogic 13y agoI had a class where we built a DB engine from scratch, and I ported my code to C# just for LINQ and proper list handling. While it's not the most efficient, it reduced many of the complex sections down to a few lines, like reordering the values being inserted to match the order necessary for a record by comparing the schema to the parameter list in the insert statement. If you like LINQ and wish it were available in JS, look at underscore or lo-dash.
- kephra 13y ago/me wonders are candidates allowed to chose their favorite language? man bash | tr '[:upper:] ' '[:lower:]\n' | sed '/^$/d' | sort | uniq -c | sort -rn | head | awk '{ print $2 }' | fmt or do you only hire windows coders?
- dpritchett 13y agoIt sounds like OP has been writing C# for ten years straight, so I'd guess there's a certain level of Windows/MSSQL/.NET experience expected.
- kyllo 13y agoPerhaps, if you did it in Powershell?
- epistasis 13y agoThis is how I do this sort of thing all the time. But, the sort is O(n log n), so it's asymptotically less satisfying.
- kephra 13y agoTrue, a bag of words would perform better. man bash | tr '[:upper:] ' '[:lower:]\n' | awk '/./ { bag[$1]++ } END { for(word in bag) { print bag[word], word } }' | sort -rn | awk '{ print } NR>=10 { exit(0) }' But it would require more typing and thinking. sure one could do this in awk completely, man bash | awk ' /./ { for (i = 1; i<=NF; i++) { bag[tolower($i)]++ } } END { for (i = 1; i<=10; i++) { score=0; for(word in bag) { if (bag[word] > score) { score=bag[word]; best=word } } printf "%s ", best delete bag[best] } printf "\n" }' if the requirement is: please chose one language and not the complete Unix babylon.
- grannyg00se 13y agoAwesome. I was going to post my two line javascript version, but instead I'm going to study this. There are three commands I've never heard of there. And it's definitely time to take a first crack at awk.
- llambda 13y agoI hate to be the bearer of bad news, but I think there may be even simpler solutions to this problem: (take 10 (reverse (sort-by (comp first rest) (frequencies (string/split ... #"\+s")))) The above is a Clojure one-liner example that I believe satisfies the original problem. So while LINQ may have simplified from the C-language family solutions he had seen, it's clearly possible to take it one step further with the expressivity of modern languages like Clojure... Edit: remember to sort! (Forgot my coffee this morning...) Edit again: and aphyr's solution is even more concise and idiomatic, where `s` is the first paragraph of the blog post: => (->> (string/split s #"\s+") frequencies (sort-by val) reverse (take 10)) (["I" 7] ["to" 6] ["the" 5] ["a" 5] ["of" 4] ["candidates" 3] ["is" 3] ["question" 3] ["in" 3] ["their" 2])
- moomin 13y agoYou'll need to sort the result of frequencies: it's an unordered map. In practice, if you use ->> and write neater LINQ, the code is very close. Although LINQ only has group and sum/reduce, no dedicated frequencies function.
- shill 13y agoOh, we can use our favorite language in our new job? Here is a Python solution. from collections import Counter Counter(s1.split(' ')).most_common(10)
- untog 13y agoWell, sure, once you're allowed to use external libraries anything is a one line solution. In JS: doStuff = require("doStuff"); var result = doStuff(theString); isn't JS so efficient?!?
- jkrems 13y agoIt looks like collections is part of the standard library: http://docs.python.org/2/library/collections.html http://docs.python.org/2/library/collections.html. I think this disqualifies your JS solution, as long as doStuff isn't some fancy nodejs core module I missed.
- inzax 13y agoHe should try adding AsParrallel() to the expression. I bet there will be a drastic speed up as long as he has a multi core.
- moomin 13y agoI hate to be the arrogant know it all on Hacker News, but seriously, if you're writing c# and not using LINQ all the time, you need to catch up. I'm tired of seeing people answer interview questions with anything _other_ than LINQ.
- kryten 13y agoLINQ is not a magic bullet. It has some serious pitfalls. I'd like to aee people using the right tools for the job.
- teh_klev 13y agoLINQ is a great mechanism for expressing complex operations in a succinct manner. Sure, LINQ may not be a magic bullet and can be abused, but you know the old adage about premature optimisation. Perhaps you could elaborate on "It has some serious pitfalls.".
- hvidgaard 13y agoI didn't write the post you responded to, but I can tell you of the main pit fall I have encountered. By far the worst is evaluating the same enumerable again and again by mistake. It don't show up in unittests because they use small data sets, but as soon as you do any kind of load test or put it into production it's painfully slow. The solution is often very simple, but you really have to get dirt on your hands before you recognize where it is a problem.
- kryten 13y agoSee https://news.ycombinator.com/item?id=6026280 https://news.ycombinator.com/item?id=6026280
- redact207 13y agoI wouldn't call you arrogant, LINQ has been around for 5 years now and any C# interview should include it. That said, not all questions are best solved with it, but it certainly has made working with enumerables much easier.
- superfx 13y agoSince we're comparing notes, here's the Mathematica version: Reverse[SortBy[Tally[StringSplit[#]], #[[2]] &]][[;; 10, 1]] &
- deleted 13y ago[deleted]
- jastr 13y agoThe O(n) solutions sound way more interesting! 1. Iterate through all key,value pairs once, keeping track of the 10 most common 2. Run quickselect 10 times 3. Coolest (and an interview question in it's own right) - modify quickselect to return the top 10!
- lubomir 13y agoThe LINQ really is much more succint, but the original code did not set the bar very high. Why should one write a 12 line comparing function when using 'b.value - a.value' would work pretty much the same (unless C# really requires comparison to return -1/1 instead of any negative/positive integer, which would be fixed by a sign function).
- ajanuary 13y agoOr do: private static int CompareKVPByCount(KeyValuePair<string, int> a, KeyValuePair<string, int> b) { return a.Value.Compare(b.Value); } Or even: kvpList.Sort(kvp => kvp.Value) But that would probably be straying into authors "list of language features I've completely ignored for the last 5 years"
- mrcozz 13y agoEveryone knows "Hadoop is a distributed system for counting words." ;-) https://github.com/twitter/scalding https://github.com/twitter/scalding
- coderguy123 13y agotext.Split(' ').Where(x=>!string.IsNullOrWhiteSpace(x)).GroupBy(x => x).Select(x => new { word = x.Key, count = x.Count() }).OrderByDescending(x => x.count).Select(x=>x.word).Take(10).ToArray();
- wtfdotnet 13y agoWhy are you allowed to ask technical question whens you dont even understand how LINQ works under the hood. This material is nearly 6 years old and even a dated Jon Skeet book would suffice. WTF.
- wtfdotnet 13y agoAny dot net dev not familiar with littered .toList need to pack their bags.
- ExpiredLink 13y ago> “Return the top 10 most frequently occurring words in a string.” ... > var words = s1.Split(' '); Wrong. Yet another example where an interviewer cannot correctly solve his own questions.
- danabramov 13y agoCare to elaborate? It probably lacks punctuation characters, case-insensitiveness and RemoveEmptyItems option, but is there anything else missing?
- jameshart 13y ago中国四分之一地区六亿人受雾霾影响 contains more than one word.
- cema 13y agoDepends on the definition of a word. I18n is normally hard, and likely would make for an exciting discussion instead of just a tech interview.
- ExpiredLink 13y ago> there anything else missing? You named more than I detected.
- Shish2k 13y agoIn the vein of http://xkcd.com/353/ http://xkcd.com/353/ : >>> from collections import Counter >>> Counter("here are some words here are".split()).most_common(3) [('are', 2), ('here', 2), ('words', 1)]
- seivan 13y agoI would have used a weighted SET. But it's funny, the first time I read the paragraph, I'd assume the words were not separated by space, and you had to find occurrences of combinations than. I instinctively made the test much harder than it would be. I'm damaged.
- deleted 13y ago[deleted]
- mythz 13y agoLINQ does make C# more readable but it doesn't add much value over normal functional collections which usually end up more concise, simpler and easier to reason about - visible in my Dart port of C#'s 101 LINQ samples (which are also lazy): https://github.com/dartist/101LinqSamples https://github.com/dartist/101LinqSamples performance of Linq 2 Objects is not that great either and it doesn't add much value readability-wise over rewriting the same task in other dynamic languages: https://github.com/dartist/sudoku_solver https://github.com/dartist/sudoku_solver
- dbaupp 13y agoI think the analysis may've been skewed by the outlying points. They are almost certainly "high leverage points"[1] and so possibly exert an undue influence on the final trend lines. [1]: http://en.wikipedia.org/wiki/Partial_leverage http://en.wikipedia.org/wiki/Partial_leverage
- joshka 13y agoThe biggest benefit I find from using Linq is readability. It allows the code to express what it is doing rather than how it is doing it. Compare: foreach (var item in list) if (SomeCondition(item)) return item; return null; vs. list.FirstOrDefault(item);
- zwieback 13y agoGood read. It also shows that interviewing can be a great learning tool. I've experienced that myself, while interviewing takes a lot of time investment on the part of the interviewer it's also often a source of new insights.
- coderguy123 13y agoisn't .ToList() part of linq too. technically the first solution is also using Linq. would be interesting to make them implement sort algorithm too.
- jahabrewer 13y ago(his name is Jon Skeet) (sorry)
- deleted 13y ago[deleted]
- navinp1912 13y agostring s,f; map<string,int> M; set<pair<int,string> > S; while(cin >> s) { M[s]++; int x=M[s]; if(x>1) S.erase(make_pair(x-1,s)); S.insert(make_pair(x,s)); } set<pair<int,string> >::reverse_iterator it=S.rbegin(); int topK=10; while(topK-- && (it!=S.rend())) { cout << it->second<<" "<<it->first<<endl; it++; }
- deleted 13y ago[deleted]
- kragen 13y agoNot bad. I think it would be a little simpler and faster with: while (cin >> s) M[s]++; for (map<string,int>::iterator i = M.begin(); i != M.end(); i++) { S.insert(make_pair(i->second, i->first)); } But maybe there's a downside to that approach that isn't obvious to me?
- navinp1912 13y agoIf you move what while (topK--) into the map loop , it becomes an online code for topK whereas what you wrote is an offline . If you want offline then pushing it into a priority_queue and then popping it out would be much faster.
- deleted 13y ago[deleted]
- aaronbrethorst 13y agoThat was a fun exercise :) input_string.split(/\W+/).inject(Hash.new(0)) {|acc, w| acc[w] += 1; acc}.sort {|a,b| b.last <=> a.last }[0,10]
- deleted 13y ago[deleted]
- tolmasky 13y agoMy main problem with LINQ is that it seems to perform terribly on mobile. I was looking for map/reduce/etc type functions for C# in Unity, and thought I found it with LINQ. To my dismay, LINQ creates so many crazy intermediate objects to pull off its "laziness" that our GC high water mark was being crossed all the time. I went and just reimplemented everything from underscore.js in C# and got way better performance. I imagine this is probably not an issue on desktops/servers.
- Locke1689 13y agoIf you'd like to share your code, I'm a functional programmer at heart and am always looking for optimization opportunities for functional-style programming in C#. If you send me some of the code you were having trouble with I can see if your use case maps well into some compiler optimization strategies.
- gecko 13y agoIt's not mobile; it's old versions of Mono having a completely shit GC. Do you know whether Unity has upgraded to SGEN yet?
- tolmasky 13y agoI don't believe so, I know we had to use the same pool workarounds that everyone seems to need due to this issue
- ajanuary 13y agoThe first projection isn't needed, which would eliminate the creation of 10 objects.
- seoguru 13y agohere's a verbose ruby version: def topx(str,x) c = Hash.new(0) str.split(/\s+/).each { |s| c[s] += 1 } c.sort_by {|k,v| -v}.take(x) end
- deleted 13y ago[deleted]
- A1kmm 13y agoI'm not sure that language features making code more succinct really ruin the question. The C# isn't even that succinct compared to doing a similar thing in other popular languages. For example, in Haskell: topTenWords :: String -> [String] topTenWords = take 10 . map fst . sortBy (flip (comparing snd)) . map (\l -> (head l, length l)) . group . sort . words
- tome 13y agoflip (comparing whatever) is a neat trick! I'll have to remember that. For \l -> (head l, length l) I tend to use head * * * length (without the spaces between those stars).
- lelf 13y agoWell, welcome to high-level programming count = take 10 . map head . reverse . sortBy (comparing length) . group . sort . words That's ignoring Unicode rules for word splitting of course
- prakashk 13y agoPerl 6: .say for (bag($text.words) ==> sort {-*.value})[^10]
- pawrvx 13y agoLINQ is my favorite API of all times. It rocks.
- olmobrutall 13y agoI totally agree. Sure there where similar things a in the funcional world before, but LinQ had some important pros: - deferred ejecution by default, saving memory and time - step by step syntax, each new operation is at the end, not the beginning - excellent type inference and intellisense, js? ruby?... - it works with the same syntax on the database!!! Haskell? - map and filter where there, but groupby and join where not so common in previous query comprehensions APIs. - the most important: it's actually usable in jobs you get paid for, not experiments you can make at home or university. There are however two things that doesnt make it 100% perfect: - expression tree lambas are identical to non expression ones, making it hard for developers to know if one step is going to be translated or executed. I would have chosen => for non expression and -> for expressions for example or something like that. - having two syntax, method chain and query comprehensions, produces a frequent anoying back and forth since some operators are better written in one (let, join, group by) while others are only available in method chain (take, toDictionary...)
- gboudrias 13y agoWhat is it with people's obsession over lines of code? As someone who doesn't do C# or LINQ, that second solution seems to me like someone really wanted to have as few lines of code as possible. I don't claim to have an impressive programming pedigree, but while I take simplicity and performance into account, I never take "conciseness" into account. Conciseness usually means "this is opaque as shit but at least it's short". And who really cares about short? What's the purpose of "short"? None that I can find, other than impressing interviewers. Anyone who believes otherwise should probably be writing in Clojure or Haskell (and probably is), but I personally just don't see the point. But that's just my opinion. My favorite language is Python.
- kragen 13y agoLess code is less places to insert bugs and less to read. The majority of time spent "writing" software is actually spent reading the existing code, so "less to read" is really important. "Concise" does not mean "short"; it means "short and clear". Obviously if your short code is opaque or bug-prone then you're defeating the purpose.
- tel 13y agoWhy does this "ruin" a question?
- freework 13y agoBecause it spoils the interviewers ability to feel smug.
- tel 13y agoHere's the Haskell golf import qualified Data.Map as M import Data.List import Data.Ord countWords :: String -> [String] countWords = map fst . take 10 . sortBy (comparing snd) . M.toList . M.fromListWith (+) . map (\w -> (w, 1)) . words
- kragen 13y agoSo, aside from the Clojure, Mathematica, Python, Ruby, Bourne Shell, Haskell, and Scala solutions posted in the other comments, all of which are simpler than the C++, C#, and JS solutions, presented here with some minor cleanups: (take 10 (reverse (sort-by (comp first rest) (frequencies (string/split ... #"\+s")))) ; llambda Clojure // haakon Scala s.split(' ').groupBy(identity).mapValues(_.size).toList.sortBy(-_._2).take(10).map(_._1) (->> (string/split s #"\s+") frequencies (sort-by val) reverse (take 10)) ; aphyr Clojure var top = (from w in text.Split(' ') // louthy C# LINQ group w by w into g orderby g.Count() descending select g.Key).Take(10); collections.Counter(s1.split()).most_common(10) # shill Python d = {} # shill Python without collections library for word in s1.split(): d[word] = d.get(word, 0) + 1 print [(x, d[x]) for x in sorted(d, key=d.get, reverse=True)][:10] words = s.split() # spenuke and abecedarius probably O(N²) Python sorted(set(words), key=words.count, reverse=True)[:10] d3.entries((s.split(" ").reduce(function(p, v){ // 1wheel JS with d3 v in p ? p[v]++ : p[v] = 1; return p;}, {}))) .sort(function(a, b){ return a.value > b.value; }) .map(function(d){ return d.key;}) .slice(-10) # kenuke O(N²) Ruby: str.split.sort_by{|word| str.split.count(word)}.uniq.reverse.take(10) counts = Hash.new { 0 } # my Ruby str.split.each { |w| counts[w] += 1; } counts.keys.sort_by { |w| -counts[w] }.take 10 # aaronbrethorst ruby str.split(/\W+/).inject(Hash.new(0)) {|acc, w| acc[w] += 1; acc}.sort {|a,b| b.last <=> a.last }[0,10] Commonest[StringSplit[string], 10] # carlob Mathematica Reverse[SortBy[Tally[StringSplit[#]], #[[2]] &]][[;; 10, 1]] & # superfx old Mathematica $a = array_count_values(preg_split('/\b\s+/', $s)); arsort($a); array_slice($a, 0, 10) // Myrth PHP tr -cs a-zA-Z '\n' | sort | uniq -c | sort -nr | head # mzs and me sh -- lelf in Haskell take 10 . map head . reverse . sortBy (comparing length) . group . sort . words # prakashk Perl6 .say for (bag($text.words) ==> sort {-*.value})[^10] # navinp1912 C++ string s,f; map<string,int> M; set<pair<int,string> > S; while(cin >> s) { M[s]++; int x=M[s]; if(x>1) S.erase(make_pair(x-1,s)); S.insert(make_pair(x,s)); } set<pair<int,string> >::reverse_iterator it=S.rbegin(); int topK=10; while(topK-- && (it!=S.rend())) { cout << it->second<<" "<<it->first<<endl; it++; } I thought I'd maybe take a look at Afterquery: http://afterquery.appspot.com/help http://afterquery.appspot.com/help Although I haven't tested it, I think the Afterquery program to solve this, assuming you first had something to tokenize your text into one word per row, would be something like &group=word;count(*) &order=-count(*) &limit=10 which, though perhaps less readable, is simpler still, except for Mathematica. More details at http://apenwarr.ca/log/?m=201212 http://apenwarr.ca/log/?m=201212. Perl 5, perhaps surprisingly, is not simpler: perl -wle 'local $/; $_ = <>; $, = " "; $w{$_}++ for split; print @{[sort {$w{$b} <=> $w{$a}} keys %w]}[0..9]' And neither is this, although it uses less code and less RAM: perl -wlne '$w{$_}++ for split; END { $, = " "; print @{[sort {$w{$b} <=> $w{$a}} keys %w]}[0..9]}' I was surprised, attempting to solve this in Common Lisp, that there's no equivalent of string/split in ANSI Common Lisp, and although SPLIT-SEQUENCE is standardized, it's not included in SBCL's default install, at least on Debian; and counting the duplicate words involves an explicit loop. So basically in unvarnished CL you end up doing more or less what you'd do in C, but without writing your own hash table. Lua and Scheme too, I think, except that in Scheme you don't even have hash tables.