1 00:00:00,460 --> 00:00:05,020 So we've acquired some new birds and need to be imported into our dataset. 2 00:00:05,020 --> 00:00:06,020 How would we go about that? 3 00:00:07,120 --> 00:00:11,140 Let's say someone has uploaded a file of birds and now we need to cross-match 4 00:00:11,140 --> 00:00:15,570 it to our dataset to see if there are any new birds we can add. 5 00:00:15,570 --> 00:00:20,549 There's another method in our bird repository 6 00:00:20,549 --> 00:00:25,278 class that gives us a list of imported birds, 7 00:00:25,278 --> 00:00:31,888 var importedBirds = BirdRepository.loadImportedBirds. 8 00:00:31,888 --> 00:00:36,347 Okay, let's take a look. 9 00:00:36,347 --> 00:00:40,200 All right, so we've got a handful. 10 00:00:40,200 --> 00:00:46,513 Let's make sure we've also got our list of current birds, 11 00:00:46,513 --> 00:00:51,318 birds = BirdRepository.LoadBirds(). 12 00:00:53,882 --> 00:00:59,522 Okay, the imported birds list has some birds that don't exist in our birds list, 13 00:00:59,522 --> 00:01:02,120 but there are also duplicates. 14 00:01:02,120 --> 00:01:04,050 In order to add them to our birds list, 15 00:01:04,050 --> 00:01:07,190 we'll need to figure out which ones don't already exist. 16 00:01:07,190 --> 00:01:09,830 The first thing I think of is using a join. 17 00:01:11,460 --> 00:01:12,857 Let's clear the console. 18 00:01:15,309 --> 00:01:19,030 Okay, let's start out with imported birds. 19 00:01:19,030 --> 00:01:24,887 Let's assign it to a variable newBirds 20 00:01:24,887 --> 00:01:31,635 = importedBirds.Join our regular birds. 21 00:01:31,635 --> 00:01:36,245 Then the outer key which is imported birds, 22 00:01:36,245 --> 00:01:40,861 I'll call it ib, goes to ib.CommonName. 23 00:01:40,861 --> 00:01:46,160 And then b, our inner key, 24 00:01:46,160 --> 00:01:51,007 goes to b.CommonName. 25 00:01:51,007 --> 00:01:58,217 And then our result (ib, 26 00:01:58,217 --> 00:02:02,804 b) goes to new { 27 00:02:02,804 --> 00:02:09,031 ImportedBirds = ib, 28 00:02:09,031 --> 00:02:12,980 Birds = b }. 29 00:02:12,980 --> 00:02:16,867 Well, we're only gonna end up with the birds that are already in our data, 30 00:02:19,061 --> 00:02:20,810 Since we're using a join. 31 00:02:20,810 --> 00:02:23,120 But we need the opposite of that. 32 00:02:23,120 --> 00:02:25,340 How can we get the birds that aren't in our data? 33 00:02:26,370 --> 00:02:29,510 We can do this by using a new type of join. 34 00:02:29,510 --> 00:02:31,330 It's called an outer join. 35 00:02:31,330 --> 00:02:36,020 The join operator we used earlier can also be called an inner join. 36 00:02:36,020 --> 00:02:39,090 An inner join combines two sequences and 37 00:02:39,090 --> 00:02:44,390 returns only the elements where the property values are equal in both sets. 38 00:02:44,390 --> 00:02:48,480 An outer join still compares two sequences based on a property value, 39 00:02:48,480 --> 00:02:51,000 but what it returns is a little different. 40 00:02:51,000 --> 00:02:54,620 Instead of returning only the elements in both sequences that share 41 00:02:54,620 --> 00:02:59,370 the property value, it returns all the elements from one sequence and 42 00:02:59,370 --> 00:03:03,210 then only the matching elements in the next sequence. 43 00:03:03,210 --> 00:03:06,500 Link will only let us use a left outer join, but 44 00:03:06,500 --> 00:03:08,150 there are other types of joins out there. 45 00:03:09,360 --> 00:03:13,120 If you want to read more about them, check out the teacher's notes. 46 00:03:13,120 --> 00:03:16,040 So in a left join, the left sequence, or 47 00:03:16,040 --> 00:03:19,990 the first one we'll use, will have all of its elements returned. 48 00:03:19,990 --> 00:03:24,430 So in order to do this in link, we'll need to use the group join operator and 49 00:03:24,430 --> 00:03:28,360 we'll use our imported birds as our left sequence since we're interested in 50 00:03:28,360 --> 00:03:31,653 the birds that have been imported but are not in our birds list yet. 51 00:03:31,653 --> 00:03:34,458 var newBirds 52 00:03:34,458 --> 00:03:43,386 = importedBirds.GroupJoin(birds, 53 00:03:43,386 --> 00:03:48,997 ib => ib.CommonName, 54 00:03:48,997 --> 00:03:54,869 and b => b.CommonName. 55 00:03:54,869 --> 00:04:01,765 So this is gonna be pretty much just like our regular join we just did. 56 00:04:01,765 --> 00:04:07,122 (ib, b) => new 57 00:04:07,122 --> 00:04:13,372 { ImportedBirds = ib, 58 00:04:13,372 --> 00:04:17,550 Birds = b}). 59 00:04:17,550 --> 00:04:20,200 Let's take a look at what that looks like. 60 00:04:20,200 --> 00:04:25,400 So we've got an innumerable of ImportedBirds, 61 00:04:25,400 --> 00:04:28,730 and then it looks like we've got a grouping of Birds. 62 00:04:28,730 --> 00:04:30,720 Notice that these groupings are empty. 63 00:04:30,720 --> 00:04:32,512 I bet those are our new birds, and 64 00:04:32,512 --> 00:04:36,380 these one down here are the ones that exist in our dataset. 65 00:04:36,380 --> 00:04:40,560 Well, we need to select only the imported birds that don't have a match. 66 00:04:40,560 --> 00:04:44,815 So first we need to flatten out our groupings of birds with a select many so 67 00:04:44,815 --> 00:04:48,665 that we can have one bird for each imported bird in our result. 68 00:04:48,665 --> 00:04:53,216 But we'll also use that default if empty operator in order to return null 69 00:04:53,216 --> 00:04:55,615 if there are no birds in the grouping. 70 00:04:55,615 --> 00:04:57,015 Do you remember that? 71 00:04:57,015 --> 00:04:58,945 Let's see how it'll work. 72 00:04:58,945 --> 00:05:00,245 Let's clear the console. 73 00:05:02,342 --> 00:05:06,491 var newBirds = 74 00:05:06,491 --> 00:05:14,791 importedBirds.GroupJoin on 75 00:05:14,791 --> 00:05:17,031 birds. 76 00:05:17,031 --> 00:05:23,238 ib => ib.CommonName, 77 00:05:23,238 --> 00:05:28,881 b => b.CommonName, 78 00:05:28,881 --> 00:05:33,675 then (ib, b) => 79 00:05:33,675 --> 00:05:39,045 new { ImportedBird. 80 00:05:39,045 --> 00:05:43,494 So I'm specifying singular here instead of what I did previously because 81 00:05:43,494 --> 00:05:49,330 the ImportedBird is going to be a single object because it's the outer sequence. 82 00:05:49,330 --> 00:05:52,890 And our inner sequence will be the grouping. 83 00:05:52,890 --> 00:05:56,558 ImportedBird = ib, and Birds, 84 00:05:56,558 --> 00:06:01,017 which is our grouping of birds, = b }. 85 00:06:04,644 --> 00:06:11,317 Then we'll throw in a SelectMany to flatten out our list of grouped birds. 86 00:06:11,317 --> 00:06:16,669 So SelectMany group of birds 87 00:06:16,669 --> 00:06:24,033 goes to gb.birds.DefaultIfEmpty. 88 00:06:25,710 --> 00:06:30,069 So DefaultIfEmpty() is going to return null if we have a birds 89 00:06:30,069 --> 00:06:32,137 grouping with nothing in it. 90 00:06:35,224 --> 00:06:38,242 Then, (gb, 91 00:06:38,242 --> 00:06:43,777 b => new { ImportedBird 92 00:06:43,777 --> 00:06:48,810 = gb.ImportedBird, 93 00:06:48,810 --> 00:06:52,090 Bird = b }). 94 00:06:55,180 --> 00:06:56,932 Okay, our new birds. 95 00:06:56,932 --> 00:07:02,640 All right, now we've got a flat list with an ImportedBird and 96 00:07:02,640 --> 00:07:06,790 then a matching bird which is null if there were no matches. 97 00:07:08,170 --> 00:07:11,540 So we only care about the imported birds without a match. 98 00:07:11,540 --> 00:07:15,517 So let's add a where clause, 99 00:07:15,517 --> 00:07:21,090 .Where(nb => nb.Bird == null). 100 00:07:23,280 --> 00:07:27,880 So that returns only the imported birds that we need. 101 00:07:27,880 --> 00:07:30,937 But let's get rid of that bird object that's null. 102 00:07:30,937 --> 00:07:34,841 Select(nb => 103 00:07:34,841 --> 00:07:39,863 nb.ImportedBird. 104 00:07:42,968 --> 00:07:44,560 Okay, great. 105 00:07:44,560 --> 00:07:50,740 So let's assign these to a new variable, var imported. 106 00:07:50,740 --> 00:07:54,720 Actually, we'll get the history of the last one we just did, and 107 00:07:54,720 --> 00:07:56,588 I'm gonna assign that to a new variable. 108 00:07:56,588 --> 00:08:05,721 var newImportedBirds 109 00:08:05,721 --> 00:08:09,180 = that, and then I'm gonna call a ToList. 110 00:08:12,548 --> 00:08:18,130 Okay, now our list is ready to import into our birds list. 111 00:08:18,130 --> 00:08:21,270 We can use the AddRange method on the birds list 112 00:08:21,270 --> 00:08:23,716 to add our new birds all at once. 113 00:08:23,716 --> 00:08:29,940 birds.AddRange(newImportedBirds. 114 00:08:32,718 --> 00:08:35,320 Okay, let's get a new count for our birds. 115 00:08:37,080 --> 00:08:39,780 Great, and our imported birds are now in our birds list.