diff --git a/.openpublishing.redirection.csharp.json b/.openpublishing.redirection.csharp.json index 318030d2bd636..8178f4f52f709 100644 --- a/.openpublishing.redirection.csharp.json +++ b/.openpublishing.redirection.csharp.json @@ -1308,6 +1308,42 @@ "source_path_from_root": "/docs/csharp/language-reference/proposals/csharp-11.0/index.md", "redirect_url": "/dotnet/csharp/language-reference/proposals/csharp-11.0/static-abstracts-in-interfaces" }, + { + "source_path_from_root": "/docs/csharp/linq/create-a-nested-group.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/grouping-data" + }, + { + "source_path_from_root": "/docs/csharp/linq/group-query-results.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/grouping-data" + }, + { + "source_path_from_root": "/docs/csharp/linq/join-by-using-composite-keys.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/join-operations" + }, + { + "source_path_from_root": "/docs/csharp/linq/perform-a-subquery-on-a-grouping-operation.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/grouping-data" + }, + { + "source_path_from_root": "/docs/csharp/linq/perform-grouped-joins.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/join-operations" + }, + { + "source_path_from_root": "/docs/csharp/linq/perform-inner-joins.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/join-operations" + }, + { + "source_path_from_root": "/docs/csharp/linq/perform-left-outer-joins.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/join-operations" + }, + { + "source_path_from_root": "/docs/csharp/linq/order-the-results-of-a-join-clause.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/" + }, + { + "source_path_from_root": "/docs/csharp/linq/perform-custom-join-operations.md", + "redirect_url": "/dotnet/csharp/linq/" + }, { "source_path_from_root": "/docs/csharp/linq/query-expression-basics.md", "redirect_url": "/dotnet/csharp/linq/get-started/query-expression-basics" @@ -1909,6 +1945,11 @@ "redirect_url": "/dotnet/standard/linq/join-two-collections", "redirect_document_id": false }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/aggregation-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/", + "redirect_document_id": false + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/applicability-of-functional-transformation.md", "redirect_url": "/dotnet/standard/linq/applicability-functional-transformation", @@ -1921,7 +1962,7 @@ }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/basic-linq-query-operations.md", - "redirect_url": "/dotnet/csharp/programming-guide/concepts/linq/standard-query-operators-overview" + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/" }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/classification-of-standard-query-operators-by-manner-of-execution.md", @@ -1929,7 +1970,7 @@ }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/query-expression-syntax-for-standard-query-operators.md", - "redirect_url": "/dotnet/csharp/programming-guide/concepts/linq/standard-query-operators-overview" + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/" }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/basic-queries-linq-to-xml.md", @@ -1956,11 +1997,21 @@ "redirect_url": "/dotnet/standard/linq/comparison-xpath-linq-xml", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/concatenation-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/", + "redirect_document_id": false + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/concepts-and-terminology-functional-transformation.md", "redirect_url": "/dotnet/standard/linq/concepts-terminology-functional-transformation", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/converting-data-types.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/converting-data-types", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/creating-the-source-office-open-xml-document.md", "redirect_url": "/dotnet/standard/linq/create-source-office-open-xml-document", @@ -1976,6 +2027,10 @@ "redirect_url": "/dotnet/standard/linq/create-xml-trees", "redirect_document_id": false }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/data-transformations-with-linq.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/" + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/deferred-execution-and-lazy-evaluation-in-linq-to-xml.md", "redirect_url": "/dotnet/standard/linq/deferred-execution-lazy-evaluation", @@ -1986,11 +2041,26 @@ "redirect_url": "/dotnet/standard/linq/deferred-execution-example", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/element-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/", + "redirect_document_id": false + }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/equality-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/", + "redirect_document_id": false + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/example-that-outputs-office-open-xml-document-parts.md", "redirect_url": "/dotnet/standard/linq/example-outputs-office-open-xml-document-parts", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/filtering-data.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/filtering-data", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/finding-text-in-word-documents.md", "redirect_url": "/dotnet/standard/linq/find-text-word-documents", @@ -2021,6 +2091,11 @@ "redirect_url": "/dotnet/standard/linq/functional-vs-procedural-programming", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/generation-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/", + "redirect_document_id": false + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/getting-started-linq-to-xml.md", "redirect_url": "/dotnet/standard/linq/linq-xml-overview", @@ -2425,11 +2500,19 @@ "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/features-that-support-linq.md", "redirect_url": "/dotnet/csharp/linq/get-started/features-that-support-linq" }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/grouping-data.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/grouping-data" + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/introduction-to-pure-functional-transformations.md", "redirect_url": "/dotnet/standard/linq/introduction-pure-functional-transformations", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/join-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/join-operations" + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/linq-and-generic-types.md", "redirect_url": "/dotnet/csharp/linq/get-started/type-relationships-in-linq-query-operations" @@ -2536,6 +2619,11 @@ "redirect_url": "/dotnet/standard/linq/parse-string", "redirect_document_id": false }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/partitioning-data.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/partitioning-data", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/performance-of-chained-queries-linq-to-xml.md", "redirect_url": "/dotnet/standard/linq/performance-chained-queries", @@ -2571,6 +2659,11 @@ "redirect_url": "/dotnet/standard/linq/project-xml-different-shape", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/projection-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/projection-operations", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/projections-and-transformations-linq-to-xml.md", "redirect_url": "/dotnet/standard/linq/work-dictionaries-linq-xml", @@ -2581,6 +2674,11 @@ "redirect_url": "/dotnet/standard/linq/introduction-pure-functional-transformations", "redirect_document_id": false }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/quantifier-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/quantifier-operations", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq.md", "redirect_url": "/dotnet/csharp/linq/get-started/write-linq-queries", @@ -2726,11 +2824,25 @@ "redirect_url": "/dotnet/standard/linq/serialize-files-textwriters-xmlwriters", "redirect_document_id": false }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/set-operations.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/set-operations", + "redirect_document_id": true + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/shape-of-wordprocessingml-documents.md", "redirect_url": "/dotnet/standard/linq/xml-shape-wordprocessingml-documents", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/sorting-data.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/sorting-data", + "redirect_document_id": true + }, + { + "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/standard-query-operators-overview.md", + "redirect_url": "/dotnet/csharp/linq/standard-query-operators/" + }, { "source_path_from_root": "/docs/csharp/programming-guide/concepts/linq/statically-compiled-queries-linq-to-xml.md", "redirect_url": "/dotnet/standard/linq/statically-compiled-queries", diff --git a/docs/core/testing/unit-testing-mstest-runner-runsettings.md b/docs/core/testing/unit-testing-mstest-runner-runsettings.md index 3b7b34f8b833b..ea44c4ec48bf1 100644 --- a/docs/core/testing/unit-testing-mstest-runner-runsettings.md +++ b/docs/core/testing/unit-testing-mstest-runner-runsettings.md @@ -30,7 +30,7 @@ The **RunConfiguration** element can include the following elements. None of the |**TestAdaptersPaths**| One or more paths to the directory where the TestAdapters are located| MSTest runner does not use the concept of test adapters and does not allow dynamic loading of extensions unless they are part of the build, and are registered in `Program.cs`, either automatically via build targets or manually. | |**TestCaseFilter**| A filter to limit tests which will run. | To filter tests use `--filter` command line option. | |**TestSessionTimeout**|Allows users to terminate a test session when it exceeds a given timeout.| There is no alternative option. | -|**DotnetHostPath**|Specify a custom path to dotnet host that is used to run the test host. | MSTest runner is not doing any additional resolving of dotnet. It depends fully on how dotnet resolves itself, which can be controlled by environment variables such as [`DOTNET_HOST_PATH`](/dotnet/core/tools/dotnet-environment-variables#dotnet_host_path). | +|**DotnetHostPath**|Specify a custom path to dotnet host that is used to run the test host. | MSTest runner is not doing any additional resolving of dotnet. It depends fully on how dotnet resolves itself, which can be controlled by environment variables such as [`DOTNET_HOST_PATH`](../tools/dotnet-environment-variables.md#dotnet_host_path). | |**TreatNoTestsAsError**| Exit with non-zero exit code when no tests are discovered. | MSTest runner will error by default when no tests are discovered or run in a test application. You can set how many tests you expect to find in the assembly by using `--minimum-expected-tests` command line parameter, which defaults to 1. | ### DataCollectionRunSettings diff --git a/docs/core/tools/troubleshoot-usage-issues.md b/docs/core/tools/troubleshoot-usage-issues.md index fa4fdc08532e4..4afd9ea7e1d0d 100644 --- a/docs/core/tools/troubleshoot-usage-issues.md +++ b/docs/core/tools/troubleshoot-usage-issues.md @@ -55,7 +55,7 @@ The .NET CLI tries to add the default location to the PATH environment variable * If you've installed the .NET Core 3.0 SDK and you've set the `DOTNET_ADD_GLOBAL_TOOLS_TO_PATH` environment variable to `false`. * If you've installed .NET Core 2.2 SDK or earlier versions, and you've set the `DOTNET_SKIP_FIRST_TIME_EXPERIENCE` environment variable to `true`. -In these scenarios or if you specified the `--tool-path` option [while installing a dotnet tool](/dotnet/core/tools/global-tools#install-a-global-tool-in-a-custom-location), the `PATH` environment variable on your machine doesn't automatically contain the path where you installed the global tool. In that case, append the tool location (for example, `$HOME/.dotnet/tools`) to the `PATH` environment variable by using whatever method your shell provides for updating environment variables. For more information, see [.NET tools](global-tools.md). +In these scenarios or if you specified the `--tool-path` option [while installing a dotnet tool](global-tools.md#install-a-global-tool-in-a-custom-location), the `PATH` environment variable on your machine doesn't automatically contain the path where you installed the global tool. In that case, append the tool location (for example, `$HOME/.dotnet/tools`) to the `PATH` environment variable by using whatever method your shell provides for updating environment variables. For more information, see [.NET tools](global-tools.md). #### Local tools diff --git a/docs/csharp/language-reference/compiler-messages/cs1936.md b/docs/csharp/language-reference/compiler-messages/cs1936.md index 8619c6f433454..d7324ef4c1dd8 100644 --- a/docs/csharp/language-reference/compiler-messages/cs1936.md +++ b/docs/csharp/language-reference/compiler-messages/cs1936.md @@ -44,4 +44,4 @@ This error typically occurs when you accidentally try to query an object of some ## See also -- [Standard Query Operators Overview](../../programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview](../../linq/standard-query-operators/index.md) diff --git a/docs/csharp/language-reference/keywords/from-clause.md b/docs/csharp/language-reference/keywords/from-clause.md index 7bc86df3b02bc..5f5747316daf2 100644 --- a/docs/csharp/language-reference/keywords/from-clause.md +++ b/docs/csharp/language-reference/keywords/from-clause.md @@ -46,7 +46,7 @@ The following example shows how two `from` clauses can be used to form a complet [!code-csharp[cscsrefQueryKeywords#3](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsCsrefQueryKeywords/CS/From.cs#3)] -For more information about join operations that use multiple `from` clauses, see [Perform left outer joins](../../linq/perform-left-outer-joins.md). +For more information about join operations that use multiple `from` clauses, see [Perform left outer joins](../../linq/standard-query-operators/join-operations.md). ## See also diff --git a/docs/csharp/language-reference/keywords/group-clause.md b/docs/csharp/language-reference/keywords/group-clause.md index 5400137ce39c0..e0993f30f585a 100644 --- a/docs/csharp/language-reference/keywords/group-clause.md +++ b/docs/csharp/language-reference/keywords/group-clause.md @@ -88,6 +88,6 @@ At compile time, `group` clauses are translated into calls to the - [Query Keywords](query-keywords.md) - [Language Integrated Query (LINQ)](../../linq/index.md) -- [Create a nested group](../../linq/create-a-nested-group.md) -- [Group query results](../../linq/group-query-results.md) -- [Perform a subquery on a grouping operation](../../linq/perform-a-subquery-on-a-grouping-operation.md) +- [Create a nested group](../../linq/standard-query-operators/grouping-data.md) +- [Group query results](../../linq/standard-query-operators/grouping-data.md) +- [Perform a subquery on a grouping operation](../../linq/standard-query-operators/grouping-data.md) diff --git a/docs/csharp/language-reference/keywords/join-clause.md b/docs/csharp/language-reference/keywords/join-clause.md index fd889b3d3a4f6..95373fe43a231 100644 --- a/docs/csharp/language-reference/keywords/join-clause.md +++ b/docs/csharp/language-reference/keywords/join-clause.md @@ -28,7 +28,7 @@ The following example shows a simple inner equijoin. This query produces a flat [!code-csharp[cscsrefQueryKeywords#24](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsCsrefQueryKeywords/CS/Join.cs#24)] -For more information, see [Perform inner joins](../../linq/perform-inner-joins.md). +For more information, see [Perform inner joins](../../linq/standard-query-operators/join-operations.md). ## Group join @@ -46,7 +46,7 @@ You can also, of course, use the result of a group join as the generator of anot [!code-csharp[cscsrefQueryKeywords#26](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsCsrefQueryKeywords/CS/Join.cs#26)] -For more information, see [Perform grouped joins](../../linq/perform-grouped-joins.md). +For more information, see [Perform grouped joins](../../linq/standard-query-operators/join-operations.md). ## Left outer join @@ -54,7 +54,7 @@ In a left outer join, all the elements in the left source sequence are returned, [!code-csharp[cscsrefQueryKeywords#27](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsCsrefQueryKeywords/CS/Join.cs#27)] -For more information, see [Perform left outer joins](../../linq/perform-left-outer-joins.md). +For more information, see [Perform left outer joins](../../linq/standard-query-operators/join-operations.md). ## The equals operator @@ -62,7 +62,7 @@ A `join` clause performs an equijoin. In other words, you can only base matches ## Non-equijoins -You can perform non-equijoins, cross joins, and other custom join operations by using multiple `from` clauses to introduce new sequences independently into a query. For more information, see [Perform custom join operations](../../linq/perform-custom-join-operations.md). +You can perform non-equijoins, cross joins, and other custom join operations by using multiple `from` clauses to introduce new sequences independently into a query. For more information, see [Perform custom join operations](/dotnet/csharp/linq/). ## Joins on object collections vs. relational tables @@ -72,7 +72,7 @@ For more information about querying across related tables in the context of [!IN ## Composite keys -You can test for equality of multiple values by using a composite key. For more information, see [Join by using composite keys](../../linq/join-by-using-composite-keys.md). Composite keys can be also used in a `group` clause. +You can test for equality of multiple values by using a composite key. For more information, see [Join by using composite keys](../../linq/standard-query-operators/join-operations.md). Composite keys can be also used in a `group` clause. ## Example @@ -88,11 +88,11 @@ A `join` clause that is not followed by `into` is translated into a [!NOTE] -> The example in this topic uses the `Student` class and `students` list from the sample code in [Query a collection of objects](query-a-collection-of-objects.md). - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/NestedGroups.cs" id="nested_groups_1"::: - -Note that three nested `foreach` loops are required to iterate over the inner elements of a nested group. -
(Hover the mouse cursor over the iteration variables, `outerGroup`, `innerGroup` and `innerGroupElement` to see their actual type.) - -## See also - -- [Language Integrated Query (LINQ)](index.md) diff --git a/docs/csharp/linq/get-started/features-that-support-linq.md b/docs/csharp/linq/get-started/features-that-support-linq.md index 6f59a1e2892cc..a1af6c473aa7b 100644 --- a/docs/csharp/linq/get-started/features-that-support-linq.md +++ b/docs/csharp/linq/get-started/features-that-support-linq.md @@ -59,7 +59,7 @@ Beginning with C# 12, you can use a [collection expression](../../language-refer For more information, see: - [Object and Collection Initializers](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) -- [Query Expression Syntax for Standard Query Operators](../../programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Query Expression Syntax for Standard Query Operators](../standard-query-operators/index.md) ## Anonymous Types diff --git a/docs/csharp/linq/get-started/introduction-to-linq-queries.md b/docs/csharp/linq/get-started/introduction-to-linq-queries.md index c52353f06170c..53a165cf2f302 100644 --- a/docs/csharp/linq/get-started/introduction-to-linq-queries.md +++ b/docs/csharp/linq/get-started/introduction-to-linq-queries.md @@ -57,7 +57,7 @@ For more information about how to create specific types of data sources, see the The query specifies what information to retrieve from the data source or sources. Optionally, a query also specifies how that information should be sorted, grouped, and shaped before being returned. A query is stored in a query variable and initialized with a query expression. You use [C# query syntax](../../language-reference/keywords/query-keywords.md) to write queries. -The query in the previous example returns all the even numbers from the integer array. The query expression contains three clauses: `from`, `where` and `select`. (If you're familiar with SQL, you noticed that the ordering of the clauses is reversed from the order in SQL.) The `from` clause specifies the data source, the `where` clause applies the filter, and the `select` clause specifies the type of the returned elements. All the query clauses are discussed in detail in this section. For now, the important point is that in LINQ, the query variable itself takes no action and returns no data. It just stores the information that is required to produce the results when the query is executed at some later point. For more information about how queries are constructed, see [Standard Query Operators Overview (C#)](../../programming-guide/concepts/linq/standard-query-operators-overview.md). +The query in the previous example returns all the even numbers from the integer array. The query expression contains three clauses: `from`, `where` and `select`. (If you're familiar with SQL, you noticed that the ordering of the clauses is reversed from the order in SQL.) The `from` clause specifies the data source, the `where` clause applies the filter, and the `select` clause specifies the type of the returned elements. All the query clauses are discussed in detail in this section. For now, the important point is that in LINQ, the query variable itself takes no action and returns no data. It just stores the information that is required to produce the results when the query is executed at some later point. For more information about how queries are constructed, see [Standard Query Operators Overview (C#)](../standard-query-operators/index.md). > [!NOTE] > Queries can also be expressed by using method syntax. For more information, see [Query Syntax and Method Syntax in LINQ](write-linq-queries.md). diff --git a/docs/csharp/linq/get-started/query-expression-basics.md b/docs/csharp/linq/get-started/query-expression-basics.md index efc1ec34bbba7..be976a4923dc6 100644 --- a/docs/csharp/linq/get-started/query-expression-basics.md +++ b/docs/csharp/linq/get-started/query-expression-basics.md @@ -165,9 +165,9 @@ A query clause might itself contain a query expression, which is sometimes refer :::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="basics19"::: -For more information, see [Perform a subquery on a grouping operation](../perform-a-subquery-on-a-grouping-operation.md). +For more information, see [Perform a subquery on a grouping operation](../standard-query-operators/grouping-data.md). ## See also - [Query keywords (LINQ)](../../language-reference/keywords/query-keywords.md) -- [Standard query operators overview](../../programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard query operators overview](../standard-query-operators/index.md) diff --git a/docs/csharp/linq/get-started/write-linq-queries.md b/docs/csharp/linq/get-started/write-linq-queries.md index d18ef16ba7031..86deb1664c96c 100644 --- a/docs/csharp/linq/get-started/write-linq-queries.md +++ b/docs/csharp/linq/get-started/write-linq-queries.md @@ -25,7 +25,7 @@ Although it looks as if includ To use extension methods, you bring them into scope with `using` directives. From your application's point of view, an extension method and a regular instance method are the same. -For more information about extension methods, see [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md). For more information about standard query operators, see [Standard Query Operators Overview (C#)](../../programming-guide/concepts/linq/standard-query-operators-overview.md). Some LINQ providers, such as [Entity Framework](/ef/core/) and LINQ to XML, implement their own standard query operators and extension methods for other types besides . +For more information about extension methods, see [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md). For more information about standard query operators, see [Standard Query Operators Overview (C#)](../standard-query-operators/index.md). Some LINQ providers, such as [Entity Framework](/ef/core/) and LINQ to XML, implement their own standard query operators and extension methods for other types besides . ## Lambda expressions diff --git a/docs/csharp/linq/group-query-results.md b/docs/csharp/linq/group-query-results.md deleted file mode 100644 index ae1e93dbebcc0..0000000000000 --- a/docs/csharp/linq/group-query-results.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Group query results (LINQ in C#) -description: Learn how to group results using LINQ in C#. -ms.date: 12/01/2016 -ms.assetid: 2e4ec27f-06fb-4de7-8973-0189906d4520 ---- -# Group query results - -Grouping is one of the most powerful capabilities of LINQ. The following examples show how to group data in various ways: - -- By a single property. - -- By the first letter of a string property. - -- By a computed numeric range. - -- By Boolean predicate or other expression. - -- By a compound key. - -In addition, the last two queries project their results into a new anonymous type that contains only the student's first and last name. For more information, see the [group clause](../language-reference/keywords/group-clause.md). - -> [!NOTE] -> The examples in this topic use the `Student` class and `students` list from the sample code in [Query a collection of objects](query-a-collection-of-objects.md). - -## Group by single property example - -The following example shows how to group source elements by using a single property of the element as the group key. In this case the key is a `string`, the student's last name. It is also possible to use a substring for the key; see [the next example](#group-by-value-example). The grouping operation uses the default equality comparer for the type. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs" id="group_query_results_1"::: - -## Group by value example - -The following example shows how to group source elements by using something other than a property of the object for the group key. In this example, the key is the first letter of the student's last name. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs" id="group_query_results_2"::: - -Note that nested foreach is required to access group items. - -## Group by a range example - -The following example shows how to group source elements by using a numeric range as a group key. The query then projects the results into an anonymous type that contains only the first and last name and the percentile range to which the student belongs. An anonymous type is used because it is not necessary to use the complete `Student` object to display the results. `GetPercentile` is a helper function that calculates a percentile based on the student's average score. The method returns an integer between 0 and 10. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs" id="group_query_results_3"::: - -Note that nested foreach required to iterate over groups and group items. - -## Group by comparison example - -The following example shows how to group source elements by using a Boolean comparison expression. In this example, the Boolean expression tests whether a student's average exam score is greater than 75. As in previous examples, the results are projected into an anonymous type because the complete source element is not needed. Note that the properties in the anonymous type become properties on the `Key` member and can be accessed by name when the query is executed. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs" id="group_query_results_4"::: - -## Group by anonymous type - -The following example shows how to use an anonymous type to encapsulate a key that contains multiple values. In this example, the first key value is the first letter of the student's last name. The second key value is a Boolean that specifies whether the student scored over 85 on the first exam. You can order the groups by any property in the key. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs" id="group_query_results_5"::: - -## See also - -- -- -- [Language Integrated Query (LINQ)](index.md) -- [group clause](../language-reference/keywords/group-clause.md) -- [Anonymous Types](../fundamentals/types/anonymous-types.md) -- [Perform a Subquery on a Grouping Operation](perform-a-subquery-on-a-grouping-operation.md) -- [Create a Nested Group](create-a-nested-group.md) -- [Grouping Data](../programming-guide/concepts/linq/grouping-data.md) diff --git a/docs/csharp/linq/group-results-by-contiguous-keys.md b/docs/csharp/linq/group-results-by-contiguous-keys.md index 05cef50c69b8e..e005a81610640 100644 --- a/docs/csharp/linq/group-results-by-contiguous-keys.md +++ b/docs/csharp/linq/group-results-by-contiguous-keys.md @@ -1,8 +1,7 @@ --- title: Group results by contiguous keys (LINQ in C#) description: How to group results by contiguous keys using LINQ in C#. -ms.date: 08/14/2018 -ms.assetid: cbda9c08-151b-4c9e-82f7-c3d7f3dac66b +ms.date: 01/23/2024 --- # Group results by contiguous keys @@ -21,11 +20,11 @@ The following example shows how to group elements into chunks that represent sub The following groups will be created in this order: -1. We, think, that -2. Linq -3. is -4. really -5. cool, ! +1. We, think, that +1. Linq +1. is +1. really +1. cool, ! The solution is implemented as an extension method that is thread-safe and that returns its results in a streaming manner. In other words, it produces its groups as it moves through the source sequence. Unlike the `group` or `orderby` operators, it can begin returning groups to the caller before all of the sequence has been read. @@ -35,9 +34,9 @@ Thread-safety is accomplished by making a copy of each group or chunk as the sou The following example shows both the extension method and the client code that uses it: -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunkextensions"::: +:::code language="csharp" source="./snippets/linq-index/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunkextensions"::: -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_client_code"::: +:::code language="csharp" source="./snippets/linq-index/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_client_code"::: ### `ChunkExtensions` class @@ -47,9 +46,9 @@ In the presented code, of `ChunkExtensions` class implementation, the `while(tru What happens in that loop is: 1. Get the key for the current Chunk, by assigning it to `key` variable: `var key = keySelector(enumerator.Current);`. The source iterator will churn through the source sequence until it finds an element with a key that doesn't match. -2. Make a new Chunk (group) object, and store it in `current` variable, that initially has one GroupItem, which is a copy of the current source element. -3. Return that Chunk. A Chunk is an `IGrouping`, which is the return value of the [`ChunkBy`](#chunk-class) method. At this point the Chunk only has the first element in its source sequence. The remaining elements will be returned only when the client code foreach's over this chunk. See `Chunk.GetEnumerator` for more info. -4. Check to see whether +1. Make a new Chunk (group) object, and store it in `current` variable, that initially has one GroupItem, which is a copy of the current source element. +1. Return that Chunk. A Chunk is an `IGrouping`, which is the return value of the [`ChunkBy`](#chunk-class) method. At this point the Chunk only has the first element in its source sequence. The remaining elements will be returned only when the client code foreach's over this chunk. See `Chunk.GetEnumerator` for more info. +1. Check to see whether
(a) the chunk has made a copy of all its source elements or
(b) the iterator has reached the end of the source sequence.
If the caller uses an inner foreach loop to iterate the chunk items, and that loop ran to completion, then the `Chunk.GetEnumerator` method will already have made copies of all chunk items before we get here. If the `Chunk.GetEnumerator` loop did not enumerate all elements in the chunk, we need to do it here to avoid corrupting the iterator for clients that may be calling us on a separate thread. @@ -58,7 +57,7 @@ What happens in that loop is: The `Chunk` class is a contiguous group of one or more source elements that have the same key. A Chunk has a key and a list of ChunkItem objects, which are copies of the elements in the source sequence: -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunk_class"::: +:::code language="csharp" source="./snippets/linq-index/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunk_class"::: A Chunk has a linked list of `ChunkItem`s, which represent the elements in the current chunk. Each `ChunkItem` (represented by `ChunkItem` class) has a reference to the next `ChunkItem` in the list. The list consists of it's `head` - which stores the contents of the first source element that belongs with this chunk, and it's `tail` - which is an end of the list. It is repositioned each time a new `ChunkItem` is added. @@ -72,7 +71,3 @@ Then, if iteration is at the end of the source, or at the end of the current chu The `CopyAllChunkElements` method is called after the end of the last chunk was reached. It first checks whether there are more elements in the source sequence. If there are, it returns `true` if enumerator for this chunk was exhausted. In this method, when private `DoneCopyingChunk` field is checked for `true`, if isLastSourceElement is `false`, it signals to the outer iterator to continue iterating. The `GetEnumerator` method of the `Chunk` class is invoked by the inner foreach loop. This method stays just one step ahead of the client requests. It adds the next element of the chunk only after the clients requests the last element in the list so far. - -## See also - -- [Language Integrated Query (LINQ)](index.md) diff --git a/docs/csharp/linq/index.md b/docs/csharp/linq/index.md index 713bb41fa5790..59eb7c217289d 100644 --- a/docs/csharp/linq/index.md +++ b/docs/csharp/linq/index.md @@ -22,7 +22,7 @@ You might need to add a [`using`](../language-reference/keywords/using-directive - Query expressions use many familiar C# language constructs, which make them easy to read. - The variables in a query expression are all strongly typed. - A query isn't executed until you iterate over the query variable, for example in a `foreach` statement. -- At compile time, query expressions are converted to standard query operator method calls according to the rules defined in the C# specification. Any query that can be expressed by using query syntax can also be expressed by using method syntax. In some cases, query syntax is more readable and concise. In others, method syntax is more readable. There's no semantic or performance difference between the two different forms. For more information, see [C# language specification](~/_csharpstandard/standard/expressions.md#1220-query-expressions) and [Standard query operators overview](../programming-guide/concepts/linq/standard-query-operators-overview.md). +- At compile time, query expressions are converted to standard query operator method calls according to the rules defined in the C# specification. Any query that can be expressed by using query syntax can also be expressed by using method syntax. In some cases, query syntax is more readable and concise. In others, method syntax is more readable. There's no semantic or performance difference between the two different forms. For more information, see [C# language specification](~/_csharpstandard/standard/expressions.md#1220-query-expressions) and [Standard query operators overview](standard-query-operators/index.md). - Some query operations, such as or , have no equivalent query expression clause and must therefore be expressed as a method call. Method syntax can be combined with query syntax in various ways. - Query expressions can be compiled to expression trees or to delegates, depending on the type that the query is applied to. queries are compiled to delegates. and queries are compiled to expression trees. For more information, see [Expression trees](/dotnet/csharp/advanced-topics/expression-trees). diff --git a/docs/csharp/linq/join-by-using-composite-keys.md b/docs/csharp/linq/join-by-using-composite-keys.md deleted file mode 100644 index cc42448cf6a6a..0000000000000 --- a/docs/csharp/linq/join-by-using-composite-keys.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Join by using composite keys (LINQ in C#) -description: Learn how to join by using composite keys in LINQ. -ms.date: 12/01/2016 -ms.assetid: da70b54d-3213-45eb-8437-fbe75cbcf935 ---- -# Join by using composite keys - -This example shows how to perform join operations in which you want to use more than one key to define a match. This is accomplished by using a composite key. You create a composite key as an anonymous type or named typed with the values that you want to compare. If the query variable will be passed across method boundaries, use a named type that overrides and for the key. The names of the properties, and the order in which they occur, must be identical in each key. - -## Example - -The following example demonstrates how to use a composite key to join data from three tables: - -```csharp -var query = from o in db.Orders - from p in db.Products - join d in db.OrderDetails - on new {o.OrderID, p.ProductID} equals new {d.OrderID, d.ProductID} into details - from d in details - select new {o.OrderID, p.ProductID, d.UnitPrice}; -``` - -Type inference on composite keys depends on the names of the properties in the keys, and the order in which they occur. If the properties in the source sequences don't have the same names, you must assign new names in the keys. For example, if the `Orders` table and `OrderDetails` table each used different names for their columns, you could create composite keys by assigning identical names in the anonymous types: - -```csharp -join...on new {Name = o.CustomerName, ID = o.CustID} equals - new {Name = d.CustName, ID = d.CustID } -``` - -Composite keys can be also used in a `group` clause. - -## See also - -- [Language Integrated Query (LINQ)](index.md) -- [join clause](../language-reference/keywords/join-clause.md) -- [group clause](../language-reference/keywords/group-clause.md) diff --git a/docs/csharp/linq/order-the-results-of-a-join-clause.md b/docs/csharp/linq/order-the-results-of-a-join-clause.md deleted file mode 100644 index 99d8832b06ffe..0000000000000 --- a/docs/csharp/linq/order-the-results-of-a-join-clause.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Order the results of a join clause (LINQ in C#) -description: Learn how to order the results of a LINQ join clause in C#. -ms.date: 12/01/2016 -ms.assetid: a7458901-1201-4c25-b8d9-c04ca52e0eb9 ---- -# Order the results of a join clause - -This example shows how to order the results of a join operation. Note that the ordering is performed after the join. Although you can use an `orderby` clause with one or more of the source sequences before the join, generally we do not recommend it. Some LINQ providers might not preserve that ordering after the join. - -> [!NOTE] -> The example in this topic uses the following data classes: -> -> :::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs" id="order_result_of_join_0"::: - -## Example - -This query creates a group join, and then sorts the groups based on the category element, which is still in scope. Inside the anonymous type initializer, a sub-query orders all the matching elements from the products sequence. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/OrderResultsOfJoin.cs" id="order_results_of_join_1"::: - -## See also - -- [Language Integrated Query (LINQ)](index.md) -- [orderby clause](../language-reference/keywords/orderby-clause.md) -- [join clause](../language-reference/keywords/join-clause.md) diff --git a/docs/csharp/linq/perform-a-subquery-on-a-grouping-operation.md b/docs/csharp/linq/perform-a-subquery-on-a-grouping-operation.md deleted file mode 100644 index 61fa2fec74b48..0000000000000 --- a/docs/csharp/linq/perform-a-subquery-on-a-grouping-operation.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Perform a subquery on a grouping operation (LINQ in C#) -description: How to perform a subquery on a grouping operation using LINQ in C#. -ms.date: 12/01/2016 -ms.assetid: d75a588e-9b6f-4f37-b195-f99ec8503855 ---- -# Perform a subquery on a grouping operation - -This article shows two different ways to create a query that orders the source data into groups, and then performs a subquery over each group individually. The basic technique in each example is to group the source elements by using a *continuation* named `newGroup`, and then generating a new subquery against `newGroup`. This subquery is run against each new group that is created by the outer query. Note that in this particular example the final output is not a group, but a flat sequence of anonymous types. - -For more information about how to group, see [group clause](../language-reference/keywords/group-clause.md). - -For more information about continuations, see [into](../language-reference/keywords/into.md). The following example uses an in-memory data structure as the data source, but the same principles apply for any kind of LINQ data source. - -## Example - -> [!NOTE] -> The examples in this topic use the `Student` class and `students` list from the sample code in [Query a collection of objects](query-a-collection-of-objects.md). - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/SubqueryOnGroup.cs" id="subquery_on_group_1"::: - -The query in the snippet above can also be written using method syntax. The following code snippet has a semantically equivalent query written using method syntax. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/SubqueryOnGroup.cs" id="subquery_on_group_2"::: - -## See also - -- [Language Integrated Query (LINQ)](index.md) diff --git a/docs/csharp/linq/perform-custom-join-operations.md b/docs/csharp/linq/perform-custom-join-operations.md deleted file mode 100644 index 6e1eeb7736111..0000000000000 --- a/docs/csharp/linq/perform-custom-join-operations.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Perform custom join operations (LINQ in C#) -description: Learn how to perform custom LINQ join operations in C#. -ms.date: 12/01/2016 -ms.assetid: 56a2a4a5-7299-497d-b3c3-23c848678911 ---- -# Perform custom join operations - -This example shows how to perform join operations that aren't possible with the `join` clause. In a query expression, the `join` clause is limited to, and optimized for, equijoins, which are by far the most common type of join operation. When performing an equijoin, you will probably always get the best performance by using the `join` clause. - -However, the `join` clause cannot be used in the following cases: - -- When the join is predicated on an expression of inequality (a non-equijoin). - -- When the join is predicated on more than one expression of equality or inequality. - -- When you have to introduce a temporary range variable for the right side (inner) sequence before the join operation. - - To perform joins that aren't equijoins, you can use multiple `from` clauses to introduce each data source independently. You then apply a predicate expression in a `where` clause to the range variable for each source. The expression also can take the form of a method call. - -> [!NOTE] -> Don't confuse this kind of custom join operation with the use of multiple `from` clauses to access inner collections. For more information, see [join clause](../language-reference/keywords/join-clause.md). - -## Cross-join - -> [!NOTE] -> This example and the one after use the `Product` and `Category` definitions from [Order the results of a join clause](order-the-results-of-a-join-clause.md). - -This query shows a simple cross join. Cross joins must be used with caution because they can produce very large result sets. However, they can be useful in some scenarios for creating source sequences against which additional queries are run. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs" id="cross_join"::: - -## Non-equijoin - -This query produces a sequence of all the products whose category ID is listed in the category list on the left side. Note the use of the `let` clause and the `Contains` method to create a temporary array. It also is possible to create the array before the query and eliminate the first `from` clause. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs" id="non_equijoin"::: - -## Merge CSV files - -In the following example, the query must join two sequences based on matching keys that, in the case of the inner (right side) sequence, cannot be obtained prior to the join clause itself. If this join were performed with a `join` clause, then the `Split` method would have to be called for each element. The use of multiple `from` clauses enables the query to avoid the overhead of the repeated method call. However, since `join` is optimized, in this particular case it might still be faster than using multiple `from` clauses. The results will vary depending primarily on how expensive the method call is. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs" id="merge_csv_files"::: - -Note that `queryNamesScores`, containing the merged data sources, in the above example is using a named type. You could use `var` instead of an explicit type for the query. -
Also, the `students` variable is optional to create. This good practice of storing the newly created `Student` objects in memory allows for faster access in future queries. - -## See also - -- [Language Integrated Query (LINQ)](index.md) -- [join clause](../language-reference/keywords/join-clause.md) -- [Order the results of a join clause](order-the-results-of-a-join-clause.md) diff --git a/docs/csharp/linq/perform-grouped-joins.md b/docs/csharp/linq/perform-grouped-joins.md deleted file mode 100644 index 58dd869f00f23..0000000000000 --- a/docs/csharp/linq/perform-grouped-joins.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Perform grouped joins (LINQ in C#) -description: Learn how to perform grouped joins using LINQ in C#. -ms.date: 04/22/2020 -ms.assetid: 9667daf9-a5fd-4b43-a5c4-a9c2b744000e ---- -# Perform grouped joins - -The group join is useful for producing hierarchical data structures. It pairs each element from the first collection with a set of correlated elements from the second collection. - -For example, a class or a relational database table named `Student` might contain two fields: `Id` and `Name`. A second class or relational database table named `Course` might contain two fields: `StudentId` and `CourseTitle`. A group join of these two data sources, based on matching `Student.Id` and `Course.StudentId`, would group each `Student` with a collection of `Course` objects (which might be empty). - -> [!NOTE] -> Each element of the first collection appears in the result set of a group join regardless of whether correlated elements are found in the second collection. In the case where no correlated elements are found, the sequence of correlated elements for that element is empty. The result selector therefore has access to every element of the first collection. This differs from the result selector in a non-group join, which cannot access elements from the first collection that have no match in the second collection. - -> [!WARNING] -> has no direct equivalent in traditional relational database terms. However, this method does implement a superset of inner joins and left outer joins. Both of these operations can be written in terms of a grouped join. For more information, see [Join Operations](../programming-guide/concepts/linq/join-operations.md) and [Entity Framework Core, GroupJoin](/ef/core/querying/complex-query-operators#groupjoin). - -The first example in this article shows you how to perform a group join. The second example shows you how to use a group join to create XML elements. - -> [!NOTE] -> The examples in this topic use the `Person` and `Pet` data classes from [Perform inner joins](perform-inner-joins.md). - -## Example - Group join - -The following example performs a group join of objects of type `Person` and `Pet` based on the `Person` matching the `Pet.Owner` property. Unlike a non-group join, which would produce a pair of elements for each match, the group join produces only one resulting object for each element of the first collection, which in this example is a `Person` object. The corresponding elements from the second collection, which in this example are `Pet` objects, are grouped into a collection. Finally, the result selector function creates an anonymous type for each match that consists of `Person.FirstName` and a collection of `Pet` objects. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupJoins.cs" id="grouped_joins_1"::: - -In the above example, `query` variable contains the query that creates a list where each element is an anonymous type that contains the person's first name and a collection of pets that are owned by them. - -## Example - Group join to create XML - -Group joins are ideal for creating XML by using LINQ to XML. The following example is similar to the previous example except that instead of creating anonymous types, the result selector function creates XML elements that represent the joined objects. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/GroupJoins.cs" id="grouped_joins_2"::: - -## See also - -- -- -- [Perform inner joins](perform-inner-joins.md) -- [Perform left outer joins](perform-left-outer-joins.md) -- [Anonymous types](../fundamentals/types/anonymous-types.md) diff --git a/docs/csharp/linq/perform-inner-joins.md b/docs/csharp/linq/perform-inner-joins.md deleted file mode 100644 index 643765e543741..0000000000000 --- a/docs/csharp/linq/perform-inner-joins.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Perform inner joins (LINQ in C#) -description: Learn how to perform inner joins using LINQ in C#. -ms.date: 12/01/2016 -ms.assetid: 45bceed6-f549-4114-a9b1-b44feb497742 ---- -# Perform inner joins - -In relational database terms, an *inner join* produces a result set in which each element of the first collection appears one time for every matching element in the second collection. If an element in the first collection has no matching elements, it doesn't appear in the result set. The method, which is called by the `join` clause in C#, implements an inner join. - -This article shows you how to perform four variations of an inner join: - -- A simple inner join that correlates elements from two data sources based on a simple key. - -- An inner join that correlates elements from two data sources based on a *composite* key. A composite key, which is a key that consists of more than one value, enables you to correlate elements based on more than one property. - -- A *multiple join* in which successive join operations are appended to each other. - -- An inner join that is implemented by using a group join. - -> [!NOTE] -> The examples in this topic use the following data classes: -> -> :::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs" id="inner_joins_0"::: -> -> as well as the `Student` class from [Query a collection of objects](query-a-collection-of-objects.md). - -## Example - Simple key join - -The following example creates two collections that contain objects of two user-defined types, `Person` and `Pet`. The query uses the `join` clause in C# to match `Person` objects with `Pet` objects whose `Owner` is that `Person`. The `select` clause in C# defines how the resulting objects will look. In this example, the resulting objects are anonymous types that consist of the owner's first name and the pet's name. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_1"::: - -You achieve the same results using the method syntax: - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_method_syntax_1"::: - -The `Person` object whose `LastName` is "Huff" doesn't appear in the result set because there's no `Pet` object that has `Pet.Owner` equal to that `Person`. - -## Example - Composite key join - -Instead of correlating elements based on just one property, you can use a composite key to compare elements based on multiple properties. To do this, specify the key selector function for each collection to return an anonymous type that consists of the properties you want to compare. If you label the properties, they must have the same label in each key's anonymous type. The properties must also appear in the same order. - -The following example uses a list of `Employee` objects and a list of `Student` objects to determine which employees are also students. Both of these types have a `FirstName` and a `LastName` property of type . The functions that create the join keys from each list's elements return an anonymous type that consists of the `FirstName` and `LastName` properties of each element. The join operation compares these composite keys for equality and returns pairs of objects from each list where both the first name and the last name match. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_2"::: - -You can use the method, as shown in the following example: - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_method_syntax_2"::: - -## Example - Multiple join - -Any number of join operations can be appended to each other to perform a multiple join. Each `join` clause in C# correlates a specified data source with the results of the previous join. - -The following example creates three collections: a list of `Person` objects, a list of `Cat` objects, and a list of `Dog` objects. - -The first `join` clause in C# matches people and cats based on a `Person` object matching `Cat.Owner`. It returns a sequence of anonymous types that contain the `Person` object and `Cat.Name`. - -The second `join` clause in C# correlates the anonymous types returned by the first join with `Dog` objects in the supplied list of dogs, based on a composite key that consists of the `Owner` property of type `Person`, and the first letter of the animal's name. It returns a sequence of anonymous types that contain the `Cat.Name` and `Dog.Name` properties from each matching pair. Because this is an inner join, only those objects from the first data source that have a match in the second data source are returned. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_3"::: - -The equivalent using multiple method uses the same approach with the anonymous type (in the example below it's named `commonOwner`): - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_method_syntax_3"::: - -## Example - Inner join by using grouped join - -The following example shows you how to implement an inner join by using a group join. - -In `query1`, the list of `Person` objects is group-joined to the list of `Pet` objects based on the `Person` matching the `Pet.Owner` property. The group join creates a collection of intermediate groups, where each group consists of a `Person` object and a sequence of matching `Pet` objects. - -By adding a second `from` clause to the query, this sequence of sequences is combined (or flattened) into one longer sequence. The type of the elements of the final sequence is specified by the `select` clause. In this example, that type is an anonymous type that consists of the `Person.FirstName` and `Pet.Name` properties for each matching pair. - -The result of `query1` is equivalent to the result set that would have been obtained by using the `join` clause without the `into` clause to perform an inner join. The `query2` variable demonstrates this equivalent query. - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_4"::: - -The same results can be achieved using method, as follows: - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_method_syntax_4"::: - -This approach requires chaining the query results with to create the final list of Owner - Pet relation based on the results of group join. To avoid chaining, the single method can be used as presented here: - -:::code language="csharp" source="../../../samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs" id="inner_joins_method_syntax_5"::: - -## See also - -- -- -- [Perform grouped joins](perform-grouped-joins.md) -- [Perform left outer joins](perform-left-outer-joins.md) -- [Anonymous types](../fundamentals/types/anonymous-types.md) diff --git a/docs/csharp/linq/perform-left-outer-joins.md b/docs/csharp/linq/perform-left-outer-joins.md deleted file mode 100644 index 284d96877ee98..0000000000000 --- a/docs/csharp/linq/perform-left-outer-joins.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Perform left outer joins (LINQ in C#) -description: Learn how to perform left outer joins using LINQ in C#. -ms.date: 03/02/2022 -ms.assetid: f542cee6-3169-4dcf-a631-3a6a79ccd473 ---- - -# Perform left outer joins - -A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the method on the results of a group join. - -> [!NOTE] -> The example in this topic uses the `Pet` and `Person` data classes from [Perform inner joins](perform-inner-joins.md). - -## Example - -The following example demonstrates how to use the method on the results of a group join to perform a left outer join. - -The first step in producing a left outer join of two collections is to perform an inner join by using a group join. (See [Perform inner joins](perform-inner-joins.md) for an explanation of this process.) In this example, the list of `Person` objects is inner-joined to the list of `Pet` objects based on a `Person` object that matches `Pet.Owner`. - -The second step is to include each element of the first (left) collection in the result set even if that element has no matches in the right collection. This is accomplished by calling on each sequence of matching elements from the group join. In this example, is called on each sequence of matching `Pet` objects. The method returns a collection that contains a single, default value if the sequence of matching `Pet` objects is empty for any `Person` object, thereby ensuring that each `Person` object is represented in the result collection. - -> [!NOTE] -> The default value for a reference type is `null`; therefore, the example checks for a null reference before accessing each element of each `Pet` collection. - -:::code source="snippets/perform-left-outer-joins/Program.cs"::: - -## See also - -- -- -- [Perform inner joins](perform-inner-joins.md) -- [Perform grouped joins](perform-grouped-joins.md) -- [Anonymous types](../fundamentals/types/anonymous-types.md) diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs b/docs/csharp/linq/snippets/linq-index/GroupByContiguousKeys.cs similarity index 97% rename from samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs rename to docs/csharp/linq/snippets/linq-index/GroupByContiguousKeys.cs index 0f76a89ad09b4..7abbac29d183b 100644 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupByContiguousKeys.cs +++ b/docs/csharp/linq/snippets/linq-index/GroupByContiguousKeys.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace LinqSamples; +namespace StandardQueryOperators; // public static class ChunkExtensions @@ -209,3 +209,11 @@ public static void GroupByContiguousKeys1() } } // + +public static class GroupByContiguousKeysExamples +{ + public static void RunAllSnippets() + { + GroupByContiguousKeys.GroupByContiguousKeys1(); + } +} diff --git a/docs/csharp/linq/snippets/perform-left-outer-joins/Program.cs b/docs/csharp/linq/snippets/perform-left-outer-joins/Program.cs deleted file mode 100644 index 05484249835be..0000000000000 --- a/docs/csharp/linq/snippets/perform-left-outer-joins/Program.cs +++ /dev/null @@ -1,41 +0,0 @@ -Person magnus = new("Magnus", "Hedlund"); -Person terry = new("Terry", "Adams"); -Person charlotte = new("Charlotte", "Weiss"); -Person arlene = new("Arlene", "Huff"); - -Pet barley = new("Barley", terry); -Pet boots = new("Boots", terry); -Pet whiskers = new("Whiskers", charlotte); -Pet bluemoon = new("Blue Moon", terry); -Pet daisy = new("Daisy", magnus); - -// Create two lists. -List people = [magnus, terry, charlotte, arlene]; -List pets = [barley, boots, whiskers, bluemoon, daisy]; - -var query = - from person in people - join pet in pets on person equals pet.Owner into gj - from subpet in gj.DefaultIfEmpty() - select new - { - person.FirstName, - PetName = subpet?.Name ?? string.Empty - }; - -foreach (var v in query) -{ - Console.WriteLine($"{v.FirstName + ":",-15}{v.PetName}"); -} - -record class Person(string FirstName, string LastName); -record class Pet(string Name, Person Owner); - -// This code produces the following output: -// -// Magnus: Daisy -// Terry: Barley -// Terry: Boots -// Terry: Blue Moon -// Charlotte: Whiskers -// Arlene: diff --git a/docs/csharp/linq/snippets/perform-left-outer-joins/perform-left-outer-joins.csproj b/docs/csharp/linq/snippets/perform-left-outer-joins/perform-left-outer-joins.csproj deleted file mode 100644 index 55bb90c67c43f..0000000000000 --- a/docs/csharp/linq/snippets/perform-left-outer-joins/perform-left-outer-joins.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - net8.0 - enable - enable - Exe - - diff --git a/docs/csharp/programming-guide/concepts/linq/converting-data-types.md b/docs/csharp/linq/standard-query-operators/converting-data-types.md similarity index 65% rename from docs/csharp/programming-guide/concepts/linq/converting-data-types.md rename to docs/csharp/linq/standard-query-operators/converting-data-types.md index 994c1ac964f34..4d0fd01d9ade4 100644 --- a/docs/csharp/programming-guide/concepts/linq/converting-data-types.md +++ b/docs/csharp/linq/standard-query-operators/converting-data-types.md @@ -1,26 +1,23 @@ --- title: "Converting Data Types (C#)" description: Conversion methods change the type of input objects. See conversion operations in LINQ queries in C#, such as Enumerable.AsEnumerable and Enumerable.OfType. -ms.date: 07/20/2015 -ms.assetid: 46e5682f-77a1-4302-8f93-a2b53c408808 +ms.date: 02/14/2024 --- # Converting Data Types (C#) Conversion methods change the type of input objects. - Conversion operations in LINQ queries are useful in a variety of applications. Following are some examples: +Conversion operations in LINQ queries are useful in various applications. Following are some examples: - The method can be used to hide a type's custom implementation of a standard query operator. - - The method can be used to enable non-parameterized collections for LINQ querying. - - The , , , and methods can be used to force immediate query execution instead of deferring it until the query is enumerated. ## Methods - The following table lists the standard query operator methods that perform data-type conversions. +The following table lists the standard query operator methods that perform data-type conversions. - The conversion methods in this table whose names start with "As" change the static type of the source collection but do not enumerate it. The methods whose names start with "To" enumerate the source collection and put the items into the corresponding collection type. +The conversion methods in this table whose names start with "As" change the static type of the source collection but don't enumerate it. The methods whose names start with "To" enumerate the source collection and put the items into the corresponding collection type. |Method Name|Description|C# Query Expression Syntax|More Information| |-----------------|-----------------|---------------------------------|----------------------| @@ -33,50 +30,23 @@ Conversion methods change the type of input objects. |ToList|Converts a collection to a . This method forces query execution.|Not applicable.|| |ToLookup|Puts elements into a (a one-to-many dictionary) based on a key selector function. This method forces query execution.|Not applicable.|| +The following examples in this article use the common data sources for this area: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + ## Query Expression Syntax Example The following code example uses an explicitly typed range variable to cast a type to a subtype before accessing a member that is available only on the subtype. -```csharp -class Plant -{ - public string Name { get; set; } -} - -class CarnivorousPlant : Plant -{ - public string TrapType { get; set; } -} - -static void Cast() -{ - Plant[] plants = - [ - new CarnivorousPlant { Name = "Venus Fly Trap", TrapType = "Snap Trap" }, - new CarnivorousPlant { Name = "Pitcher Plant", TrapType = "Pitfall Trap" }, - new CarnivorousPlant { Name = "Sundew", TrapType = "Flypaper Trap" }, - new CarnivorousPlant { Name = "Waterwheel Plant", TrapType = "Snap Trap" } - ]; - - var query = from CarnivorousPlant cPlant in plants - where cPlant.TrapType == "Snap Trap" - select cPlant; - - foreach (Plant plant in query) - Console.WriteLine(plant.Name); - - /* This code produces the following output: - - Venus Fly Trap - Waterwheel Plant - */ -} -``` +:::code language="csharp" source="./snippets/standard-query-operators/ConversionExamples.cs" id="CastOperatorQuerySyntax"::: + +The equivalent query can be expressed using method syntax as shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/ConversionExamples.cs" id="CastOperatorMethodSyntax"::: ## See also - -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [from clause](../../../language-reference/keywords/from-clause.md) -- [LINQ Query Expressions](../../../linq/index.md) -- [How to query an ArrayList with LINQ (C#)](./how-to-query-an-arraylist-with-linq.md) +- [from clause](../../language-reference/keywords/from-clause.md) diff --git a/docs/csharp/linq/standard-query-operators/filtering-data.md b/docs/csharp/linq/standard-query-operators/filtering-data.md new file mode 100644 index 0000000000000..95a6f30309693 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/filtering-data.md @@ -0,0 +1,35 @@ +--- +title: "Filtering Data with LINQ" +description: Filtering, also known as selection, restricts results based on a condition. Learn about the standard query operator methods in LINQ in C# that perform filtering. +ms.date: 02/14/2024 +--- +# Filtering Data in C# with LINQ + +Filtering refers to the operation of restricting the result set to contain only those elements that satisfy a specified condition. It's also referred to as *selecting* elements that match the specified condition. + +The following illustration shows the results of filtering a sequence of characters. The predicate for the filtering operation specifies that the character must be 'A'. + +:::image type="content" source="./media/filtering-data/linq-filter-operation.png" alt-text="Diagram that shows a LINQ filtering operation"::: + +The standard query operator methods that perform selection are listed in the following table: + +|Method Name|Description|C# Query Expression Syntax|More Information| +|-----------------|-----------------|---------------------------------|----------------------| +|OfType|Selects values, depending on their ability to be cast to a specified type.|Not applicable.|

| +|Where|Selects values that are based on a predicate function.|`where`|

| + +The following example uses the `where` clause to filter from an array those strings that have a specific length. + +:::code language="csharp" source="./snippets/standard-query-operators/WhereFilter.cs" id="FilterExampleQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/WhereFilter.cs" id="FilterExampleMethod"::: + +## See also + +- +- [where clause](../../language-reference/keywords/where-clause.md) +- [How to query an assembly's metadata with Reflection (LINQ) (C#)](../../advanced-topics/reflection-and-attributes/how-to-query-assembly-metadata-with-reflection-linq.md) +- [How to query for files with a specified attribute or name (C#)](../../programming-guide/concepts/linq/how-to-query-for-files-with-a-specified-attribute-or-name.md) +- [How to sort or filter text data by any word or field (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-sort-or-filter-text-data-by-any-word-or-field-linq.md) diff --git a/docs/csharp/linq/standard-query-operators/grouping-data.md b/docs/csharp/linq/standard-query-operators/grouping-data.md new file mode 100644 index 0000000000000..cf98a00a22611 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/grouping-data.md @@ -0,0 +1,129 @@ +--- +title: "Grouping Data (C#)" +description: Grouping puts data into groups of elements that share an attribute. Learn about the standard query operator methods in LINQ in C# that group data elements. +ms.date: 02/16/2024 +--- +# Grouping Data (C#) + +Grouping refers to the operation of putting data into groups so that the elements in each group share a common attribute. The following illustration shows the results of grouping a sequence of characters. The key for each group is the character. + +:::image type="content" source="./media/grouping-data/linq-group-operation.png" alt-text="Diagram that shows a LINQ Grouping operation"::: + +The standard query operator methods that group data elements are listed in the following table. + +|Method Name|Description|C# Query Expression Syntax|More Information| +|-----------------|-----------------|---------------------------------|----------------------| +|GroupBy|Groups elements that share a common attribute. An object represents each group.|`group … by`

-or-

`group … by … into …`|

| +|ToLookup|Inserts elements into a (a one-to-many dictionary) based on a key selector function.|Not applicable.|| + +The following code example uses the `group by` clause to group integers in a list according to whether they're even or odd. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupOverview.cs" id="OverviewSampleQuerySyntax"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupOverview.cs" id="OverviewSampleMethodSyntax"::: + +The following examples in this article use the common data sources for this area: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + +## Group query results + +Grouping is one of the most powerful capabilities of LINQ. The following examples show how to group data in various ways: + +- By a single property. +- By the first letter of a string property. +- By a computed numeric range. +- By Boolean predicate or other expression. +- By a compound key. + +In addition, the last two queries project their results into a new anonymous type that contains only the student's first and family name. For more information, see the [group clause](../../language-reference/keywords/group-clause.md). + +### Group by single property example + +The following example shows how to group source elements by using a single property of the element as the group key. The key is an `enum`, the student's year in school. The grouping operation uses the default equality comparer for the type. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByPropertyQuery"::: + +The equivalent code using method syntax is shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByPropertyMethod"::: + +### Group by value example + +The following example shows how to group source elements by using something other than a property of the object for the group key. In this example, the key is the first letter of the student's family name. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByValueQuery"::: + +Nested foreach is required to access group items. + +The equivalent code using method syntax is shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByValueMethod"::: + +### Group by a range example + +The following example shows how to group source elements by using a numeric range as a group key. The query then projects the results into an anonymous type that contains only the first and family name and the percentile range to which the student belongs. An anonymous type is used because it isn't necessary to use the complete `Student` object to display the results. `GetPercentile` is a helper function that calculates a percentile based on the student's average score. The method returns an integer between 0 and 10. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByRangeQuery"::: + +Nested foreach required to iterate over groups and group items. The equivalent code using method syntax is shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByRangeMethod"::: + +### Group by comparison example + +The following example shows how to group source elements by using a Boolean comparison expression. In this example, the Boolean expression tests whether a student's average exam score is greater than 75. As in previous examples, the results are projected into an anonymous type because the complete source element isn't needed. The properties in the anonymous type become properties on the `Key` member. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByBooleanQuerySyntax"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByBooleanMethodSyntax"::: + +### Group by anonymous type + +The following example shows how to use an anonymous type to encapsulate a key that contains multiple values. In this example, the first key value is the first letter of the student's family name. The second key value is a Boolean that specifies whether the student scored over 85 on the first exam. You can order the groups by any property in the key. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByCompundKeyQuerySyntax"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupQueryResults.cs" id="GroupByCompundKeyMethodSyntax"::: + +## Create a nested group + +The following example shows how to create nested groups in a LINQ query expression. Each group that is created according to student year or grade level is then further subdivided into groups based on the individuals' names. + +:::code language="csharp" source="./snippets/standard-query-operators/NestedGroups.cs" id="NestedGroupsQuerySyntax"::: + +Three nested `foreach` loops are required to iterate over the inner elements of a nested group. +
(Hover the mouse cursor over the iteration variables, `outerGroup`, `innerGroup`, and `innerGroupElement` to see their actual type.) + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/NestedGroups.cs" id="NestedGroupsMethodSyntax"::: + +## Perform a subquery on a grouping operation + +This article shows two different ways to create a query that orders the source data into groups, and then performs a subquery over each group individually. The basic technique in each example is to group the source elements by using a *continuation* named `newGroup`, and then generating a new subquery against `newGroup`. This subquery is run against each new group created by the outer query. In this particular example the final output isn't a group, but a flat sequence of anonymous types. + +For more information about how to group, see [group clause](../../language-reference/keywords/group-clause.md). For more information about continuations, see [into](../../language-reference/keywords/into.md). The following example uses an in-memory data structure as the data source, but the same principles apply for any kind of LINQ data source. + +:::code language="csharp" source="./snippets/standard-query-operators/SubqueryOnGroup.cs" id="SubQueryOnGroupQuerySyntax"::: + +The query in the preceding snippet can also be written using method syntax. The following code snippet has a semantically equivalent query written using method syntax. + +:::code language="csharp" source="./snippets/standard-query-operators/SubqueryOnGroup.cs" id="SubQueryOnGroupMethodSyntax"::: + +## See also + +- +- +- +- [group clause](../../language-reference/keywords/group-clause.md) +- [How to group files by extension (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-group-files-by-extension-linq.md) +- [How to split a file into many files by using groups (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-split-a-file-into-many-files-by-using-groups-linq.md) diff --git a/docs/csharp/linq/standard-query-operators/index.md b/docs/csharp/linq/standard-query-operators/index.md new file mode 100644 index 0000000000000..0d227e132dd6e --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/index.md @@ -0,0 +1,105 @@ +--- +title: "Standard Query Operators Overview (C#)" +description: The LINQ standard query operators provide query capabilities including filtering, projection, aggregation, and sorting in C#. +ms.date: 02/16/2024 +--- +# Standard Query Operators Overview + +The *standard query operators* are the keywords and methods that form the LINQ pattern. The C# language defines [LINQ query keywords](../../language-reference/keywords/query-keywords.md) that you use for the most common query expression. The compiler translates expressions using these keywords to the equivalent method calls. The two forms are synonymous. Other methods that are part of the namespace don't have equivalent query keywords. In those cases, you must use the method syntax. This section covers all the query operator keywords. The runtime and other NuGet packages add more methods designed to work with LINQ queries each release. The most common methods, including those that have query keyword equivalents are covered in this section. For the full list of query methods supported by the .NET Runtime, see the API documentation. In addition to the methods covered here, this class contains methods for concatenating data sources, computing a single value from a data source, such as a sum, average, or other value. + +Most of these methods operate on sequences, where a sequence is an object whose type implements the interface or the interface. The standard query operators provide query capabilities including filtering, projection, aggregation, sorting and more. The methods that make up each set are static members of the and classes, respectively. They're defined as [*extension methods*](../../programming-guide/classes-and-structs/extension-methods.md) of the type that they operate on. + +The distinction between and sequences determines how the query is executed at runtime. + +For `IEnumerable`, the returned enumerable object captures the arguments that were passed to the method. The returned enumerable object captures the arguments that were passed to the method. When that object is enumerated, the logic of the query operator is employed and the query results are returned. + +For `IQueryable`, the query is translated into an [expression tree](../../advanced-topics/expression-trees/index.md). The expression tree can be translated to a native query when the data source can optimize the query. Libraries such as [Entity Framework](/ef/core/) translate LINQ queries into native SQL queries that execute at the database. + +The following code example demonstrates how the standard query operators can be used to obtain information about a sequence. + +:::code language="csharp" source="./snippets/standard-query-operators/IndexExamples.cs" id="FirstSentence"::: + +Where possible, the queries in this section use a sequence of words or numbers as the input source. For queries where more complicated relationships between objects are used, the following sources that model a school are used: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + +## Types of query operators + +The standard query operators differ in the timing of their execution, depending on whether they return a singleton value or a sequence of values. Those methods that return a singleton value (such as and ) execute immediately. Methods that return a sequence defer the query execution and return an enumerable object. You can use the output sequence of one query as the input sequence to another query. Calls to query methods can be chained together in one query, which enables queries to become arbitrarily complex. + +## Query operators + +In a LINQ query, the first step is to specify the data source. In a LINQ query, the `from` clause comes first in order to introduce the data source (`customers`) and the *range variable* (`cust`). + +:::code language="csharp" source="./snippets/standard-query-operators/IndexExamples.cs" id="ObtainDataSource"::: + +The range variable is like the iteration variable in a `foreach` loop except that no actual iteration occurs in a query expression. When the query is executed, the range variable serves as a reference to each successive element in `customers`. Because the compiler can infer the type of `cust`, you don't have to specify it explicitly. You can introduce more range variables in a `let` clause. For more information, see [let clause](../../language-reference/keywords/let-clause.md). + +> [!NOTE] +> For non-generic data sources such as , the range variable must be explicitly typed. For more information, see [How to query an ArrayList with LINQ (C#)](../../programming-guide/concepts/linq/how-to-query-an-arraylist-with-linq.md) and [from clause](../../language-reference/keywords/from-clause.md). + +Once you obtain a data source, you can perform any number of operations on that data source: + +- [Filter data](filtering-data.md) using the `where` keyword. +- [Order data](sorting-data.md) using the `orderby` and optionally `descending` keywords. +- [Group data](grouping-data.md) using the `group` and optionally `into` keywords. +- [Join data](join-operations.md) using the `join` keyword. +- [Project data](projection-operations.md) using the `select` keyword. + +## Query Expression Syntax Table + +The following table lists the standard query operators that have equivalent query expression clauses. + +| Method | C# query expression syntax | +|------------|---------------------------------| +||Use an explicitly typed range variable:

`from int i in numbers`

(For more information, see [from clause](../../language-reference/keywords/from-clause.md).)| +||`group … by`

-or-

`group … by … into …`

(For more information, see [group clause](../../language-reference/keywords/group-clause.md).)| +||`join … in … on … equals … into …`

(For more information, see [join clause](../../language-reference/keywords/join-clause.md).)| +||`join … in … on … equals …`

(For more information, see [join clause](../../language-reference/keywords/join-clause.md).)| +||`orderby`

(For more information, see [orderby clause](../../language-reference/keywords/orderby-clause.md).)| +||`orderby … descending`

(For more information, see [orderby clause](../../language-reference/keywords/orderby-clause.md).)| +||`select`

(For more information, see [select clause](../../language-reference/keywords/select-clause.md).)| +||Multiple `from` clauses.

(For more information, see [from clause](../../language-reference/keywords/from-clause.md).)| +||`orderby …, …`

(For more information, see [orderby clause](../../language-reference/keywords/orderby-clause.md).)| +||`orderby …, … descending`

(For more information, see [orderby clause](../../language-reference/keywords/orderby-clause.md).)| +||`where`

(For more information, see [where clause](../../language-reference/keywords/where-clause.md).)| + +## Data Transformations with LINQ + +Language-Integrated Query (LINQ) isn't only about retrieving data. It's also a powerful tool for transforming data. By using a LINQ query, you can use a source sequence as input and modify it in many ways to create a new output sequence. You can modify the sequence itself without modifying the elements themselves by sorting and grouping. But perhaps the most powerful feature of LINQ queries is the ability to create new types. The [select](../../language-reference/keywords/select-clause.md) clause creates an output element from an input element. You use it to transform an input element into an output element: + +- Merge multiple input sequences into a single output sequence that has a new type. +- Create output sequences whose elements consist of only one or several properties of each element in the source sequence. +- Create output sequences whose elements consist of the results of operations performed on the source data. +- Create output sequences in a different format. For example, you can transform data from SQL rows or text files into XML. + +These transformations can be combined in various ways in the same query. Furthermore, the output sequence of one query can be used as the input sequence for a new query. The following example transforms objects in an in-memory data structure into XML elements. + +:::code language="csharp" source="./snippets/standard-query-operators/IndexExamples.cs" id="XmlTransformation"::: + +The code produces the following XML output: + +:::code language="xml" source="./snippets/standard-query-operators/IndexExamples.cs" id="XmlTransformationOutput"::: + +For more information, see [Creating XML Trees in C# (LINQ to XML)](../../../standard/linq/create-xml-trees.md). + +You can use the results of one query as the data source for a subsequent query. This example shows how to order the results of a join operation. This query creates a group join, and then sorts the groups based on the category element, which is still in scope. Inside the anonymous type initializer, a subquery orders all the matching elements from the products sequence. + +:::code language="csharp" source="./snippets/standard-query-operators/OrderResultsOfJoin.cs" id="OrderResultsOfJoinQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/OrderResultsOfJoin.cs" id="OrderResultsOfJoinMethod"::: + +Although you can use an `orderby` clause with one or more of the source sequences before the join, generally we don't recommend it. Some LINQ providers might not preserve that ordering after the join. For more information, see [join clause](../../language-reference/keywords/join-clause.md). + +## See also + +- +- +- [select clause](../../language-reference/keywords/select-clause.md) +- [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md) +- [Query Keywords (LINQ)](../../language-reference/keywords/query-keywords.md) +- [Anonymous Types](../../fundamentals/types/anonymous-types.md) diff --git a/docs/csharp/linq/standard-query-operators/join-operations.md b/docs/csharp/linq/standard-query-operators/join-operations.md new file mode 100644 index 0000000000000..9233844aebac4 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/join-operations.md @@ -0,0 +1,175 @@ +--- +title: "Join Operations (C#)" +description: A join of two data sources associates objects with objects that share an attribute across data sources. Learn about join methods in the LINQ framework in C#. +ms.date: 02/16/2024 +no-loc: [Join, GroupJoin] +--- +# Join Operations in LINQ + +A *join* of two data sources is the association of objects in one data source with objects that share a common attribute in another data source. + +Joining is an important operation in queries that target data sources whose relationships to each other can't be followed directly. In object-oriented programming, joining could mean a correlation between objects that isn't modeled, such as the backwards direction of a one-way relationship. An example of a one-way relationship is a `Student` class that has a property of type `Department` that represents the major, but the `Department` class doesn't have a property that is a collection of `Student` objects. If you have a list of `Department` objects and you want to find all the students in each department, you could use a join operation to find them. + +The join methods provided in the LINQ framework are and . These methods perform equijoins, or joins that match two data sources based on equality of their keys. (For comparison, Transact-SQL supports join operators other than `equals`, for example the `less than` operator.) In relational database terms, implements an inner join, a type of join in which only those objects that have a match in the other data set are returned. The method has no direct equivalent in relational database terms, but it implements a superset of inner joins and left outer joins. A left outer join is a join that returns each element of the first (left) data source, even if it has no correlated elements in the other data source. + +The following illustration shows a conceptual view of two sets and the elements within those sets that are included in either an inner join or a left outer join. + +:::image type="content" source="./media/join-operations/join-method-overlapping-circles.png" alt-text="Two overlapping circles showing inner/outer."::: + +## Methods + +|Method Name|Description|C# Query Expression Syntax|More Information| +|-----------------|-----------------|---------------------------------|----------------------| +|Join|Joins two sequences based on key selector functions and extracts pairs of values.|`join … in … on … equals …`|

| +|GroupJoin|Joins two sequences based on key selector functions and groups the resulting matches for each element.|`join … in … on … equals … into …`|

| + +The following examples in this article use the common data sources for this area: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + +The following example uses the `join … in … on … equals …` clause to join two sequences based on specific value: + +:::code language="csharp" source="./snippets/standard-query-operators/JoinOverviewExamples.cs" id="JoinQuerySyntax"::: + +The preceding query can be expressed using method syntax as shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/JoinOverviewExamples.cs" id="JoinMethodSyntax"::: + +The following example uses the `join … in … on … equals … into …` clause to join two sequences based on specific value and groups the resulting matches for each element: + +:::code language="csharp" source="./snippets/standard-query-operators/JoinOverviewExamples.cs" id="GroupJoinQuerySyntax"::: + +The preceding query can be expressed using method syntax as shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/JoinOverviewExamples.cs" id="GroupJoinMethodSyntax"::: + +## Perform inner joins + +In relational database terms, an *inner join* produces a result set in which each element of the first collection appears one time for every matching element in the second collection. If an element in the first collection has no matching elements, it doesn't appear in the result set. The method, which is called by the `join` clause in C#, implements an inner join. The following examples show you how to perform four variations of an inner join: + +- A simple inner join that correlates elements from two data sources based on a simple key. +- An inner join that correlates elements from two data sources based on a *composite* key. A composite key, which is a key that consists of more than one value, enables you to correlate elements based on more than one property. +- A *multiple join* in which successive join operations are appended to each other. +- An inner join that is implemented by using a group join. + +### Single key join + +The following example matches `Teacher` objects with `Deparment` objects whose `TeacherId` matches that `Teacher`. The `select` clause in C# defines how the resulting objects look. In the following example, the resulting objects are anonymous types that consist of the department name and the name of the teacher that leads the department. + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="SimpleInnerJoinQuery"::: + +You achieve the same results using the method syntax: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="SimpleInnerJoinMethod"::: + +The teachers who aren't department heads don't appear in the final results. + +### Composite key join + +Instead of correlating elements based on just one property, you can use a composite key to compare elements based on multiple properties. Specify the key selector function for each collection to return an anonymous type that consists of the properties you want to compare. If you label the properties, they must have the same label in each key's anonymous type. The properties must also appear in the same order. + +The following example uses a list of `Teacher` objects and a list of `Student` objects to determine which teachers are also students. Both of these types have properties that represent the first and family name of each person. The functions that create the join keys from each list's elements return an anonymous type that consists of the properties. The join operation compares these composite keys for equality and returns pairs of objects from each list where both the first name and the family name match. + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="CompositeKeyQuery"::: + +You can use the method, as shown in the following example: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="CompositeKeyMethod"::: + +### Multiple join + +Any number of join operations can be appended to each other to perform a multiple join. Each `join` clause in C# correlates a specified data source with the results of the previous join. + +The first `join` clause matches students and departments based on a `Student` object's `DepartmentID` matching a `Department` object's `ID`. It returns a sequence of anonymous types that contain the `Student` object and `Department` object. + +The second `join` clause correlates the anonymous types returned by the first join with `Teacher` objects based on that teacher's ID matching the department head ID. It returns a sequence of anonymous types that contain the student's name, the department name, and the department leader's name. Because this operation is an inner join, only those objects from the first data source that have a match in the second data source are returned. + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="MultipleJoinQuery"::: + +The equivalent using multiple method uses the same approach with the anonymous type: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="MultipleJoinMethod"::: + +### Inner join by using grouped join + +The following example shows you how to implement an inner join by using a group join. The list of `Department` objects is group-joined to the list of `Student` objects based on the `Department.ID` matching the `Student.DepartmentID` property. The group join creates a collection of intermediate groups, where each group consists of a `Department` object and a sequence of matching `Student` objects. The second `from` clause combines (or flattens) this sequence of sequences into one longer sequence. The `select` clause specifies the type of elements in the final sequence. That type is an anonymous type that consists of the student's name and the matching department name. + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="InnerGroupJoinQuery"::: + +The same results can be achieved using method, as follows: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="InnerGroupJoinMethod"::: + +The result is equivalent to the result set obtained by using the `join` clause without the `into` clause to perform an inner join. The following code demonstrates this equivalent query: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="InnerJoinQuery"::: + +To avoid chaining, the single method can be used as presented here: + +:::code language="csharp" source="./snippets/standard-query-operators/InnerJoins.cs" id="InnerJoinMethod"::: + +## Perform grouped joins + +The group join is useful for producing hierarchical data structures. It pairs each element from the first collection with a set of correlated elements from the second collection. + +> [!NOTE] +> Each element of the first collection appears in the result set of a group join regardless of whether correlated elements are found in the second collection. In the case where no correlated elements are found, the sequence of correlated elements for that element is empty. The result selector therefore has access to every element of the first collection. This differs from the result selector in a non-group join, which cannot access elements from the first collection that have no match in the second collection. + +> [!WARNING] +> has no direct equivalent in traditional relational database terms. However, this method does implement a superset of inner joins and left outer joins. Both of these operations can be written in terms of a grouped join. For more information, see [Entity Framework Core, GroupJoin](/ef/core/querying/complex-query-operators#groupjoin). + +The first example in this article shows you how to perform a group join. The second example shows you how to use a group join to create XML elements. + +### Group join + +The following example performs a group join of objects of type `Department` and `Student` based on the `Deoartment.ID` matching the `Student.DepartmentID` property. Unlike a non-group join, which produces a pair of elements for each match, the group join produces only one resulting object for each element of the first collection, which in this example is a `Department` object. The corresponding elements from the second collection, which in this example are `Student` objects, are grouped into a collection. Finally, the result selector function creates an anonymous type for each match that consists of `Department.Name` and a collection of `Student` objects. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupJoins.cs" id="GroupJoinQuery"::: + +In the above example, `query` variable contains the query that creates a list where each element is an anonymous type that contains the department's name and a collection of students that study in that department. + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupJoins.cs" id="GroupJoinMethod"::: + +### Group join to create XML + +Group joins are ideal for creating XML by using LINQ to XML. The following example is similar to the previous example except that instead of creating anonymous types, the result selector function creates XML elements that represent the joined objects. + +:::code language="csharp" source="./snippets/standard-query-operators/GroupJoins.cs" id="GroupJoinToXmlQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/GroupJoins.cs" id="GroupJoinToXmlMethod"::: + +## Perform left outer joins + +A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the method on the results of a group join. + +The following example demonstrates how to use the method on the results of a group join to perform a left outer join. + +The first step in producing a left outer join of two collections is to perform an inner join by using a group join. (See [Perform inner joins](#perform-inner-joins) for an explanation of this process.) In this example, the list of `Department` objects is inner-joined to the list of `Student` objects based on a `Department` object's ID that matches the student's `DepartmentID`. + +The second step is to include each element of the first (left) collection in the result set even if that element has no matches in the right collection. This is accomplished by calling on each sequence of matching elements from the group join. In this example, is called on each sequence of matching `Student` objects. The method returns a collection that contains a single, default value if the sequence of matching `Student` objects is empty for any `Department` object, ensuring that each `Department` object is represented in the result collection. + +> [!NOTE] +> The default value for a reference type is `null`; therefore, the example checks for a null reference before accessing each element of each `Student` collection. + +:::code source="./snippets/standard-query-operators/LeftOuterJoins.cs" id="LeftOuterJoinQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code source="./snippets/standard-query-operators/LeftOuterJoins.cs" id="LeftOuterJoinMethod"::: + +## See also + +- +- +- [Anonymous types](../../fundamentals/types/anonymous-types.md) +- [Formulate Joins and Cross-Product Queries](../../../framework/data/adonet/sql/linq/formulate-joins-and-cross-product-queries.md) +- [join clause](../../language-reference/keywords/join-clause.md) +- [group clause](../../language-reference/keywords/group-clause.md) +- [How to join content from dissimilar files (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-join-content-from-dissimilar-files-linq.md) +- [How to populate object collections from multiple sources (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-populate-object-collections-from-multiple-sources-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/media/filtering-data/linq-filter-operation.png b/docs/csharp/linq/standard-query-operators/media/filtering-data/linq-filter-operation.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/filtering-data/linq-filter-operation.png rename to docs/csharp/linq/standard-query-operators/media/filtering-data/linq-filter-operation.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/grouping-data/linq-group-operation.png b/docs/csharp/linq/standard-query-operators/media/grouping-data/linq-group-operation.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/grouping-data/linq-group-operation.png rename to docs/csharp/linq/standard-query-operators/media/grouping-data/linq-group-operation.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/join-operations/join-method-overlapping-circles.png b/docs/csharp/linq/standard-query-operators/media/join-operations/join-method-overlapping-circles.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/join-operations/join-method-overlapping-circles.png rename to docs/csharp/linq/standard-query-operators/media/join-operations/join-method-overlapping-circles.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/partitioning-data/linq-partitioning-operations.png b/docs/csharp/linq/standard-query-operators/media/partitioning-data/linq-partitioning-operations.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/partitioning-data/linq-partitioning-operations.png rename to docs/csharp/linq/standard-query-operators/media/partitioning-data/linq-partitioning-operations.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/projection-operations/select-action-graphic.png b/docs/csharp/linq/standard-query-operators/media/projection-operations/select-action-graphic.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/projection-operations/select-action-graphic.png rename to docs/csharp/linq/standard-query-operators/media/projection-operations/select-action-graphic.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/projection-operations/select-many-action-graphic.png b/docs/csharp/linq/standard-query-operators/media/projection-operations/select-many-action-graphic.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/projection-operations/select-many-action-graphic.png rename to docs/csharp/linq/standard-query-operators/media/projection-operations/select-many-action-graphic.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/quantifier-operations/linq-quantifier-operations.png b/docs/csharp/linq/standard-query-operators/media/quantifier-operations/linq-quantifier-operations.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/quantifier-operations/linq-quantifier-operations.png rename to docs/csharp/linq/standard-query-operators/media/quantifier-operations/linq-quantifier-operations.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/set-operations/distinct-method-behavior.png b/docs/csharp/linq/standard-query-operators/media/set-operations/distinct-method-behavior.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/set-operations/distinct-method-behavior.png rename to docs/csharp/linq/standard-query-operators/media/set-operations/distinct-method-behavior.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/set-operations/except-behavior-graphic.png b/docs/csharp/linq/standard-query-operators/media/set-operations/except-behavior-graphic.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/set-operations/except-behavior-graphic.png rename to docs/csharp/linq/standard-query-operators/media/set-operations/except-behavior-graphic.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/set-operations/intersection-two-sequences.png b/docs/csharp/linq/standard-query-operators/media/set-operations/intersection-two-sequences.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/set-operations/intersection-two-sequences.png rename to docs/csharp/linq/standard-query-operators/media/set-operations/intersection-two-sequences.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/set-operations/union-operation-two-sequences.png b/docs/csharp/linq/standard-query-operators/media/set-operations/union-operation-two-sequences.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/set-operations/union-operation-two-sequences.png rename to docs/csharp/linq/standard-query-operators/media/set-operations/union-operation-two-sequences.png diff --git a/docs/csharp/programming-guide/concepts/linq/media/sorting-data/alphabetical-sort-operation.png b/docs/csharp/linq/standard-query-operators/media/sorting-data/alphabetical-sort-operation.png similarity index 100% rename from docs/csharp/programming-guide/concepts/linq/media/sorting-data/alphabetical-sort-operation.png rename to docs/csharp/linq/standard-query-operators/media/sorting-data/alphabetical-sort-operation.png diff --git a/docs/csharp/programming-guide/concepts/linq/partitioning-data.md b/docs/csharp/linq/standard-query-operators/partitioning-data.md similarity index 53% rename from docs/csharp/programming-guide/concepts/linq/partitioning-data.md rename to docs/csharp/linq/standard-query-operators/partitioning-data.md index 678cc7b3eff3d..5e8853695c150 100644 --- a/docs/csharp/programming-guide/concepts/linq/partitioning-data.md +++ b/docs/csharp/linq/standard-query-operators/partitioning-data.md @@ -1,17 +1,15 @@ --- title: "Partitioning data (C#)" description: Learn how to partition data in LINQ. View an illustration showing the results of partitioning operations. -ms.date: 09/10/2021 -ms.assetid: 2a5c507b-fe22-443c-a768-dec7f9ec568d +ms.date: 02/14/2024 --- - # Partitioning data (C#) Partitioning in LINQ refers to the operation of dividing an input sequence into two sections, without rearranging the elements, and then returning one of the sections. The following illustration shows the results of three different partitioning operations on a sequence of characters. The first operation returns the first three elements in the sequence. The second operation skips the first three elements and returns the remaining elements. The third operation skips the first two elements in the sequence and returns the next three elements. -![Illustration that shows three LINQ partitioning operations.](./media/partitioning-data/linq-partitioning-operations.png) +:::image type="content" source="./media/partitioning-data/linq-partitioning-operations.png" alt-text="Illustration that shows three LINQ partitioning operations."::: The standard query operator methods that partition sequences are listed in the following section. @@ -20,16 +18,32 @@ The standard query operator methods that partition sequences are listed in the f | Method names | Description | C# query expression syntax | More information | |--|--|--|--| | Skip | Skips elements up to a specified position in a sequence. | Not applicable. |
| -| SkipWhile | Skips elements based on a predicate function until an element does not satisfy the condition. | Not applicable. |
| +| SkipWhile | Skips elements based on a predicate function until an element doesn't satisfy the condition. | Not applicable. |
| | Take | Takes elements up to a specified position in a sequence. | Not applicable. |
| -| TakeWhile | Takes elements based on a predicate function until an element does not satisfy the condition. | Not applicable. |
| +| TakeWhile | Takes elements based on a predicate function until an element doesn't satisfy the condition. | Not applicable. |
| | Chunk | Splits the elements of a sequence into chunks of a specified maximum size. | Not applicable. |
| -## Example +All the following examples use to generate a sequence of numbers from 0 through 7. + +You use the `Take` method to take only the first elements in a sequence: + +:::code source="snippets/standard-query-operators/PartitionExamples.cs" id="Take"::: + +You use the `Skip` method to skip the first elements in a sequence, and use the remaining elements: + +:::code source="snippets/standard-query-operators/PartitionExamples.cs" id="Skip"::: + +The `TakeWhile` and `SkipWhile` methods also take and skip elements in a sequence. However, instead of a set number of elements, these methods skip or take elements based on a condition. `TakeWhile` takes the elements of a sequence until an element doesn't match the condition. + +:::code source="snippets/standard-query-operators/PartitionExamples.cs" id="TakeWhile"::: + +`SkipWhile` skips the first elements, as long as the condition is true. The first element not matching the condition, and all subsequent elements, are returned. + +:::code source="snippets/standard-query-operators/PartitionExamples.cs" id="SkipWhile"::: The `Chunk` operator is used to split elements of a sequence based on a given `size`. -:::code source="snippets/partition/Program.cs"::: +:::code source="snippets/standard-query-operators/PartitionExamples.cs" id="Chunk"::: The preceding C# code: @@ -39,4 +53,3 @@ The preceding C# code: ## See also - -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) diff --git a/docs/csharp/linq/standard-query-operators/projection-operations.md b/docs/csharp/linq/standard-query-operators/projection-operations.md new file mode 100644 index 0000000000000..3457304ae2170 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/projection-operations.md @@ -0,0 +1,104 @@ +--- +title: "Projection operations in LINQ" +description: Learn about projection operations. These operations transform an object into a new form that often consists only of properties used later. +ms.date: 02/14/2024 +--- +# Projection operations (C#) + +Projection refers to the operation of transforming an object into a new form that often consists only of those properties subsequently used. By using projection, you can construct a new type that is built from each object. You can project a property and perform a mathematical function on it. You can also project the original object without changing it. + +The standard query operator methods that perform projection are listed in the following section. + +## Methods + +| Method names | Description | C# query expression syntax | More information | +|--|--|--|--| +| Select | Projects values that are based on a transform function. | `select` |
| +| SelectMany | Projects sequences of values that are based on a transform function and then flattens them into one sequence. | Use multiple `from` clauses |
| +| Zip | Produces a sequence of tuples with elements from 2-3 specified sequences. | Not applicable. |
| + +## `Select` + +The following example uses the `select` clause to project the first letter from each string in a list of strings. + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectSimpleQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectSimpleMethod"::: + +## `SelectMany` + +The following example uses multiple `from` clauses to project each word from each string in a list of strings. + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectManyQuery"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectManyMethod"::: + +The `SelectMany` method can also form the combination of matching every item in the first sequence with every item in the second sequence: + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectManyQuery2"::: + +The equivalent query using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectManyMethod2"::: + +## `Zip` + +There are several overloads for the `Zip` projection operator. All of the `Zip` methods work on sequences of two or more possibly heterogenous types. The first two overloads return tuples, with the corresponding positional type from the given sequences. + +Consider the following collections: + +:::code language="csharp" source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="NumbersAndLetters"::: + +To project these sequences together, use the operator: + +:::code source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="ZipTuple"::: + +> [!IMPORTANT] +> The resulting sequence from a zip operation is never longer in length than the shortest sequence. The `numbers` and `letters` collections differ in length, and the resulting sequence omits the last element from the `numbers` collection, as it has nothing to zip with. + +The second overload accepts a `third` sequence. Let's create another collection, namely `emoji`: + +:::code source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="Emoji"::: + +To project these sequences together, use the operator: + +:::code source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="ZipTriple"::: + +Much like the previous overload, the `Zip` method projects a tuple, but this time with three elements. + +The third overload accepts a `Func` argument that acts as a results selector. You can project a new resulting sequence from the sequences being zipped. + +:::code source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="ZipResultSelector"::: + +With the preceding `Zip` overload, the specified function is applied to the corresponding elements `numbers` and `letter`, producing a sequence of the `string` results. + +## `Select` versus `SelectMany` + +The work of both `Select` and `SelectMany` is to produce a result value (or values) from source values. `Select` produces one result value for every source value. The overall result is therefore a collection that has the same number of elements as the source collection. In contrast, `SelectMany` produces a single overall result that contains concatenated subcollections from each source value. The transform function that is passed as an argument to `SelectMany` must return an enumerable sequence of values for each source value. `SelectMany` concatenates these enumerable sequences to create one large sequence. + +The following two illustrations show the conceptual difference between the actions of these two methods. In each case, assume that the selector (transform) function selects the array of flowers from each source value. + +This illustration depicts how `Select` returns a collection that has the same number of elements as the source collection. + +:::image type="content" source="./media/projection-operations/select-action-graphic.png" alt-text="Graphic that shows the action of Select()"::: + +This illustration depicts how `SelectMany` concatenates the intermediate sequence of arrays into one final result value that contains each value from each intermediate array. + +:::image type="content" source="./media/projection-operations/select-many-action-graphic.png" alt-text="Graphic showing the action of SelectMany()"::: + +### Code example + +The following example compares the behavior of `Select` and `SelectMany`. The code creates a "bouquet" of flowers by taking the items from each list of flower names in the source collection. In the following example, the "single value" that the transform function uses is a collection of values. This example requires the extra `foreach` loop in order to enumerate each string in each subsequence. + +:::code source="./snippets/standard-query-operators/SelectProjectionExamples.cs" id="SelectVsSelectMany"::: + +## See also + +- +- [select clause](../../language-reference/keywords/select-clause.md) +- [How to populate object collections from multiple sources (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-populate-object-collections-from-multiple-sources-linq.md) +- [How to split a file into many files by using groups (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-split-a-file-into-many-files-by-using-groups-linq.md) diff --git a/docs/csharp/linq/standard-query-operators/quantifier-operations.md b/docs/csharp/linq/standard-query-operators/quantifier-operations.md new file mode 100644 index 0000000000000..6dffccf43ab85 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/quantifier-operations.md @@ -0,0 +1,43 @@ +--- +title: "Quantifier Operations (C#)" +titleSuffix: LINQ +description: Learn about quantifier operations in LINQ. These methods, `All`, `Any`, and `Contains`, return a Boolean value indicating whether some or all elements in a sequence satisfy a condition. +ms.date: 02/14/2024 +--- +# Quantifier operations in LINQ (C#) + +Quantifier operations return a value that indicates whether some or all of the elements in a sequence satisfy a condition. + +The following illustration depicts two different quantifier operations on two different source sequences. The first operation asks if any of the elements are the character 'A'. The second operation asks if all the elements are the character 'A'. Both methods return `true` in this example. + +:::image type="content" source="./media/quantifier-operations/linq-quantifier-operations.png" alt-text="LINQ Quantifier Operations"::: + +|Method Name|Description|C# Query Expression Syntax|More Information| +|-----------------|-----------------|---------------------------------|----------------------| +|All|Determines whether all the elements in a sequence satisfy a condition.|Not applicable.|
| +|Any|Determines whether any elements in a sequence satisfy a condition.|Not applicable.|
| +|Contains|Determines whether a sequence contains a specified element.|Not applicable.|
| + +## All + +The following example uses the `All` to find students that scored above 70 on all exams. + +:::code language="csharp" source="./snippets/standard-query-operators/QuantifierExamples.cs" id="AllQuantifier"::: + +## Any + +The following example uses the `Any` to find students that scored greater than 96 on an exam. + +:::code language="csharp" source="./snippets/standard-query-operators/QuantifierExamples.cs" id="AnyQuantifier"::: + +## Contains + +The following example uses the `Contains` to find students that scored exactly 95 on an exam. + +:::code language="csharp" source="./snippets/standard-query-operators/QuantifierExamples.cs" id="ContainsQuantifier"::: + +## See also + +- +- [Dynamically specify predicate filters at run time](../dynamically-specify-predicate-filters-at-runtime.md) +- [How to query for sentences that contain a specified set of words (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-query-for-sentences-that-contain-a-specified-set-of-words-linq.md) diff --git a/docs/csharp/linq/standard-query-operators/set-operations.md b/docs/csharp/linq/standard-query-operators/set-operations.md new file mode 100644 index 0000000000000..22b0c384d99b5 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/set-operations.md @@ -0,0 +1,94 @@ +--- +title: "Set operations (C#)" +description: Learn about set operations and the standard query operator methods that perform set operations in LINQ in C#. +ms.date: 01/22/2024 +--- +# Set operations (C#) + +Set operations in LINQ refer to query operations that produce a result set based on the presence or absence of equivalent elements within the same or separate collections. + +| Method names | Description | C# query expression syntax | More information | +|--|--|--|--| +| `Distinct` or `DistinctBy` | Removes duplicate values from a collection. | Not applicable. |


| +| `Except` or `ExceptBy` | Returns the set difference, which means the elements of one collection that don't appear in a second collection. | Not applicable. |


| +| `Intersect` or `IntersectBy` | Returns the set intersection, which means elements that appear in each of two collections. | Not applicable. |


| +| `Union` or `UnionBy` | Returns the set union, which means unique elements that appear in either of two collections. | Not applicable. |


| + +## `Distinct` and `DistinctBy` + +The following example depicts the behavior of the method on a sequence of strings. The returned sequence contains the unique elements from the input sequence. + +:::image type="content" source="./media/set-operations/distinct-method-behavior.png" alt-text="Graphic showing the behavior of Distinct()"::: + +:::code language="csharp;" source="snippets/standard-query-operators/SetOperations.cs" id="Distinct"::: + +The [`DistinctBy`](xref:System.Linq.Enumerable.DistinctBy%2A?displayProperty=nameWithType) is an alternative approach to `Distinct` that takes a `keySelector`. The `keySelector` is used as the comparative discriminator of the source type. In the following code, words are discriminated based on their `Length`, and the first word of each length is displayed: + +:::code source="./snippets/standard-query-operators/SetOperations.cs" id="DistinctBy"::: + +## `Except` and `ExceptBy` + +The following example depicts the behavior of . The returned sequence contains only the elements from the first input sequence that aren't in the second input sequence. + +:::image type="content" source="./media/set-operations/except-behavior-graphic.png" alt-text="Graphic showing the action of Except()"::: + +The following examples in this article use the common data sources for this area: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + +:::code language="csharp" source="./snippets/standard-query-operators/SetOperations.cs" id="Except"::: + +The method is an alternative approach to `Except` that takes two sequences of possibly heterogenous types and a `keySelector`. The `keySelector` is the same type as the first collection's type. Consider the following `Teacher` array and teacher IDs to exclude. To find teachers in the first collection that aren't in the second collection, you can project the teacher's ID onto the second collection: + +:::code source="snippets/standard-query-operators/SetOperations.cs" id="ExceptBy"::: + +In the preceding C# code: + +- The `teachers` array is filtered to only those teachers that aren't in the `teachersToExclude` array. +- The `teachersToExclude` array contains the `ID` value for all department heads. +- The call to `ExceptBy` results in a new set of values that are written to the console. + +The new set of values is of type `Teacher`, which is the type of the first collection. Each `teacher` in the `teachers` array that doesn't have a corresponding ID value in the `teachersToExclude` array is written to the console. + +## `Intersect` and `IntersectBy` + +The following example depicts the behavior of . The returned sequence contains the elements that are common to both of the input sequences. + +:::image type="content" source="./media/set-operations/intersection-two-sequences.png" alt-text="Graphic showing the intersection of two sequences"::: + +:::code language="csharp;" source="./snippets/standard-query-operators/SetOperations.cs" id="Intersect"::: + +The method is an alternative approach to `Intersect` that takes two sequences of possibly heterogenous types and a `keySelector`. The `keySelector` is used as the comparative discriminator of the second collection's type. Consider the following student and teacher arrays. The query matches items in each sequence by name to find those students who aren't also teachers: + +:::code source="./snippets/standard-query-operators/SetOperations.cs" id="IntersectBy"::: + +In the preceding C# code: + +- The query produces the intersection of the `Teacher` and `Student` by comparing names. +- Only people that are found in both arrays are present in the resulting sequence. +- The resulting `Student` instances are written to the console. + +## `Union` and `UnionBy` + +The following example depicts a union operation on two sequences of strings. The returned sequence contains the unique elements from both input sequences. + +:::image type="content" source="./media/set-operations/union-operation-two-sequences.png" alt-text="Graphic showing the union of two sequences."::: + +:::code language="csharp;" source="./snippets/standard-query-operators/SetOperations.cs" id="Union"::: + +The method is an alternative approach to `Union` that takes two sequences of the same type and a `keySelector`. The `keySelector` is used as the comparative discriminator of the source type. The following query produces the list of all people that are either students or teachers. Students who are also teachers are added to the union set only once: + +:::code source="./snippets/standard-query-operators/SetOperations.cs" id="UnionBy"::: + +In the preceding C# code: + +- The `teachers` and `students` arrays are woven together using their names as the key selector. +- The resulting names are written to the console. + +## See also + +- +- [How to combine and compare string collections (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-combine-and-compare-string-collections-linq.md) +- [How to find the set difference between two lists (LINQ) (C#)](../../programming-guide/concepts/linq/how-to-find-the-set-difference-between-two-lists-linq.md) diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/ConversionExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/ConversionExamples.cs new file mode 100644 index 0000000000000..e03ba9d3a101d --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/ConversionExamples.cs @@ -0,0 +1,46 @@ +using System.Collections; + +namespace StandardQueryOperators; + +public class ConversionExamples +{ + private static readonly IEnumerable students = Sources.Students; + public static void RunAllSnippets() + { + Console.WriteLine("ConversionExamples:"); + CastExampleQuery(); + CastExampleMethod(); + } + + private static void CastExampleQuery() + { + // + IEnumerable people = students; + + var query = from Student student in students + where student.Year == GradeLevel.ThirdYear + select student; + + foreach (Student student in query) + { + Console.WriteLine(student.FirstName); + } + // + } + + private static void CastExampleMethod() + { + // + IEnumerable people = students; + + var query = people + .Cast() + .Where(student => student.Year == GradeLevel.ThirdYear); + + foreach (Student student in query) + { + Console.WriteLine(student.FirstName); + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/DataSources.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/DataSources.cs new file mode 100644 index 0000000000000..2f563c799f61b --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/DataSources.cs @@ -0,0 +1,134 @@ +namespace StandardQueryOperators; + +// +public enum GradeLevel +{ + FirstYear = 1, + SecondYear, + ThirdYear, + FourthYear +}; + +public class Student +{ + public required string FirstName { get; init; } + public required string LastName { get; init; } + public required int ID { get; init; } + + public required GradeLevel Year { get; init; } + public required List Scores { get; init; } + + public required int DepartmentID { get; init; } +} + +public class Teacher +{ + public required string First { get; init; } + public required string Last { get; init; } + public required int ID { get; init; } + public required string City { get; init; } +} +public class Department +{ + public required string Name { get; init; } + public int ID { get; init; } + + public required int TeacherID { get; init; } +} +// + +public static class Sources +{ + public static IEnumerable Departments => + [ + new() { Name = "English", ID = 1, TeacherID = 901 }, + new() { Name = "Mathematics", ID = 2, TeacherID = 965 }, + new() { Name = "Engineering", ID = 3, TeacherID = 932 }, + new() { Name = "Economics", ID = 4, TeacherID = 945 }, + new() { Name = "Physics", ID = 5, TeacherID = 987 }, + new() { Name = "Chemistry", ID = 6, TeacherID = 901 } + ]; + + // Create a data source by using a collection initializer. + public static IEnumerable Students => + [ + new() { FirstName = "Svetlana", LastName = "Omelchenko", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 111, Scores = [97, 90, 73, 54] }, + new() { FirstName = "Claire", LastName = "O'Donnell", DepartmentID = -1, Year = GradeLevel.FirstYear, ID = 112, Scores = [56, 78, 95, 95] }, + new() { FirstName = "Sven", LastName = "Mortensen", DepartmentID = 3, Year = GradeLevel.SecondYear, ID = 113, Scores = [61, 52, 48, 72] }, + new() { FirstName = "Cesar", LastName = "Garcia", DepartmentID = 4, Year = GradeLevel.SecondYear, ID = 114, Scores = [71, 86, 77, 97] }, + new() { FirstName = "Debra", LastName = "Garcia", DepartmentID = 5, Year = GradeLevel.ThirdYear, ID = 115, Scores = [66, 96, 70, 69] }, + new() { FirstName = "Fadi", LastName = "Fakhouri", DepartmentID = 6, Year = GradeLevel.ThirdYear, ID = 116, Scores = [93, 72, 62, 65] }, + new() { FirstName = "Hanying", LastName = "Feng", DepartmentID = 1, Year = GradeLevel.FourthYear, ID = 117, Scores = [53, 81, 81, 50] }, + new() { FirstName = "Hugo", LastName = "Garcia", DepartmentID = 2, Year = GradeLevel.FourthYear, ID = 118, Scores = [68, 91, 60, 51] }, + new() { FirstName = "Lance", LastName = "Tucker", DepartmentID = -1, Year = GradeLevel.FirstYear, ID = 119, Scores = [83, 42, 68, 63] }, + new() { FirstName = "Terry", LastName = "Adams", DepartmentID = 4, Year = GradeLevel.SecondYear, ID = 120, Scores = [63, 91, 71, 51] }, + new() { FirstName = "Eugene", LastName = "Zabokritski", DepartmentID = 5, Year = GradeLevel.ThirdYear, ID = 121, Scores = [56, 40, 73, 75] }, + new() { FirstName = "Michael", LastName = "Tucker", DepartmentID = 3, Year = GradeLevel.FourthYear, ID = 122, Scores = [85, 82, 81, 70] }, + new() { FirstName = "Mark", LastName = "Johansson", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 123, Scores = [82, 94, 84, 65] }, + new() { FirstName = "Sarah", LastName = "Andersson", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 124, Scores = [77, 83, 67, 90] }, + new() { FirstName = "Christiane", LastName = "Jensen", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 125, Scores = [88, 57, 65, 87] }, + new() { FirstName = "Evgeniya", LastName = "Maslova", DepartmentID = 2, Year = GradeLevel.SecondYear, ID = 126, Scores = [46, 84, 87, 66] }, + new() { FirstName = "Innocenty", LastName = "Popov", DepartmentID = 6, Year = GradeLevel.SecondYear, ID = 127, Scores = [92, 45, 88, 60] }, + new() { FirstName = "Anna", LastName = "Hedlund", DepartmentID = 2, Year = GradeLevel.SecondYear, ID = 128, Scores = [85, 54, 74, 75] }, + new() { FirstName = "Erik", LastName = "Axelsson", DepartmentID = 3, Year = GradeLevel.ThirdYear, ID = 129, Scores = [51, 78, 54, 49] }, + new() { FirstName = "Anna", LastName = "Yermolayeva", DepartmentID = 3, Year = GradeLevel.ThirdYear, ID = 130, Scores = [46, 73, 93, 68] }, + new() { FirstName = "Cassie", LastName = "Hicks", DepartmentID = 3, Year = GradeLevel.ThirdYear, ID = 131, Scores = [85, 60, 85, 56] }, + new() { FirstName = "Ifeanacho", LastName = "Jamuike", DepartmentID = 4, Year = GradeLevel.FourthYear, ID = 132, Scores = [54, 98, 56, 61] }, + new() { FirstName = "Carmen", LastName = "Vella", DepartmentID = 6, Year = GradeLevel.FourthYear, ID = 133, Scores = [63, 57, 69, 70] }, + new() { FirstName = "Noel", LastName = "Svensson", DepartmentID = 4, Year = GradeLevel.FourthYear, ID = 134, Scores = [85, 60, 80, 73] }, + new() { FirstName = "Sanna", LastName = "Hansson", DepartmentID = 5, Year = GradeLevel.FirstYear, ID = 135, Scores = [46, 94, 93, 45] }, + new() { FirstName = "Sofiya", LastName = "Seleznyova", DepartmentID = 6, Year = GradeLevel.FirstYear, ID = 136, Scores = [74, 45, 87, 55] }, + new() { FirstName = "Amy E.", LastName = "Edwards", DepartmentID = 5, Year = GradeLevel.FirstYear, ID = 137, Scores = [87, 59, 55, 70] }, + new() { FirstName = "Nancy", LastName = "Engström", DepartmentID = 3, Year = GradeLevel.FirstYear, ID = 138, Scores = [75, 73, 78, 83] }, + new() { FirstName = "Katerina", LastName = "Kovaleva", DepartmentID = 2, Year = GradeLevel.FirstYear, ID = 139, Scores = [44, 50, 47, 41] }, + new() { FirstName = "Ifunanya", LastName = "Ugomma", DepartmentID = 4, Year = GradeLevel.FirstYear, ID = 140, Scores = [84, 82, 96, 80] }, + new() { FirstName = "Don", LastName = "Richardson", DepartmentID = 5, Year = GradeLevel.FirstYear, ID = 141, Scores = [47, 91, 73, 68] }, + new() { FirstName = "Josephine", LastName = "Balzan", DepartmentID = 6, Year = GradeLevel.SecondYear, ID = 142, Scores = [40, 47, 63, 42] }, + new() { FirstName = "Michelle", LastName = "Caruana", DepartmentID = -1, Year = GradeLevel.FirstYear, ID = 143, Scores = [97, 92, 69, 77] }, + new() { FirstName = "Gaby", LastName = "Frost", DepartmentID = 2, Year = GradeLevel.FirstYear, ID = 144, Scores = [70, 79, 47, 79] }, + new() { FirstName = "Erna", LastName = "Nilsson", DepartmentID = 3, Year = GradeLevel.FirstYear, ID = 145, Scores = [56, 52, 51, 51] }, + new() { FirstName = "Naima", LastName = "Larsson", DepartmentID = 4, Year = GradeLevel.ThirdYear, ID = 146, Scores = [65, 81, 44, 61] }, + new() { FirstName = "Donald", LastName = "Urquhart", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 147, Scores = [92, 90, 95, 57] }, + new() { FirstName = "Nadezhda", LastName = "Kolpakova", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 148, Scores = [94, 69, 52, 58] }, + new() { FirstName = "Ruth", LastName = "Olsson", DepartmentID = 2, Year = GradeLevel.FourthYear, ID = 149, Scores = [66, 49, 82, 74] }, + new() { FirstName = "Maria", LastName = "Sammut", DepartmentID = 2, Year = GradeLevel.FirstYear, ID = 150, Scores = [43, 83, 94, 60] }, + new() { FirstName = "Veronika", LastName = "Berg", DepartmentID = 3, Year = GradeLevel.FirstYear, ID = 151, Scores = [59, 76, 65, 92] }, + new() { FirstName = "Nicholas", LastName = "Micallef", DepartmentID = 3, Year = GradeLevel.FirstYear, ID = 152, Scores = [44, 57, 52, 63] }, + new() { FirstName = "Izuchukwu", LastName = "Adaobi", DepartmentID = 4, Year = GradeLevel.SecondYear, ID = 153, Scores = [51, 40, 42, 54] }, + new() { FirstName = "Nwanneka", LastName = "Ifeoma", DepartmentID = 4, Year = GradeLevel.FirstYear, ID = 154, Scores = [44, 70, 98, 56] }, + new() { FirstName = "John", LastName = "Falzon", DepartmentID = 5, Year = GradeLevel.FirstYear, ID = 155, Scores = [77, 65, 83, 45] }, + new() { FirstName = "Martina", LastName = "Mattsson", DepartmentID = 5, Year = GradeLevel.ThirdYear, ID = 156, Scores = [51, 49, 96, 72] }, + new() { FirstName = "Jeanette", LastName = "Berggren", DepartmentID = 4, Year = GradeLevel.FirstYear, ID = 157, Scores = [41, 67, 46, 68] }, + new() { FirstName = "Anastasiya", LastName = "Sazonova", DepartmentID = 2, Year = GradeLevel.FirstYear, ID = 158, Scores = [53, 96, 76, 49] }, + new() { FirstName = "Bruce", LastName = "Keever", DepartmentID = 3, Year = GradeLevel.FirstYear, ID = 159, Scores = [54, 81, 84, 81] }, + new() { FirstName = "Sami", LastName = "Åkesson", DepartmentID = 5, Year = GradeLevel.FourthYear, ID = 160, Scores = [45, 85, 79, 94] }, + new() { FirstName = "Jesper", LastName = "Jakobsson", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 161, Scores = [59, 98, 47, 92] }, + new() { FirstName = "Max", LastName = "Lindgren", DepartmentID = 2, Year = GradeLevel.FirstYear, ID = 162, Scores = [86, 88, 96, 63] }, + new() { FirstName = "Arina", LastName = "Ivanova", DepartmentID = 1, Year = GradeLevel.FirstYear, ID = 163, Scores = [93, 63, 70, 80] } + ]; + + public static IEnumerable Teachers => + [ + new() { First = "Ann", Last = "Beebe", ID = 901, City = "Seattle" }, + new() { First = "Alex", Last = "Robinson", ID = 910, City = "Redmond" }, + new() { First = "Michiyo", Last = "Sato", ID = 921, City = "Tacoma" }, + new() { First = "Aylin", Last = "Lundgren", ID = 932, City = "Seattle" }, + new() { First = "Gleb", Last = "Seleznyov", ID = 943, City = "Redmond" }, + new() { First = "Alexandra", Last = "Voronova", ID = 954, City = "Tacoma" }, + new() { First = "Kevin", Last = "McDowell", ID = 965, City = "Redmond" }, + new() { First = "Selma", Last = "Nyberg", ID = 976, City = "Seattle" }, + new() { First = "Selma", Last = "Åstrom", ID = 987, City = "Redmond" }, + new() { First = "Hillevi", Last = "Bengtsson", ID = 998, City = "Tacoma" }, + new() { First = "Allan", Last = "Sandberg", ID = 912, City = "Seattle" }, + new() { First = "Dmitry", Last = "Degtyarev", ID = 923, City = "Redmond" }, + new() { First = "Arinze", Last = "Madu", ID = 934, City = "Seattle" }, + new() { First = "Andrey", Last = "Glazkov", ID = 945, City = "Redmond" }, + new() { First = "Kettil", Last = "Berggren", ID = 956, City = "Tacoma" }, + new() { First = "Hanying", Last = "Feng", ID = 967, City = "Seattle" }, + new() { First = "Hugo", Last = "Garcia", ID = 978, City = "Redmond" }, + new() { First = "Michael", Last = "Tucker", ID = 989, City = "Tacoma" }, + new() { First = "Ifeanacho", Last = "Jamuike", ID = 991, City = "Redmond" }, + new() { First = "Carmen", Last = "Vella", ID = 982, City = "Tacoma" }, + new() { First = "Noel", Last = "Svensson", ID = 973, City = "Seattle" } + ]; +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupJoins.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupJoins.cs new file mode 100644 index 0000000000000..acf4b2bec1d72 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupJoins.cs @@ -0,0 +1,104 @@ +using System.Xml.Linq; + +namespace StandardQueryOperators; +public static class GroupJoins +{ + private static readonly IEnumerable departments = Sources.Departments; + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Group Join Query Syntax"); + GroupJoinQuerySyntax(); + Console.WriteLine("Group Join Method Syntax"); + GroupJoinMethodSyntax(); + Console.WriteLine("Group Join to XML Query Syntax"); + GroupJoinToXmlQuerySyntax(); + Console.WriteLine("Group Join to XML Method Syntax"); + GroupJoinToXmlMethodSyntax(); + } + + private static void GroupJoinQuerySyntax() + { + // + var query = from department in departments + join student in students on department.ID equals student.DepartmentID into studentGroup + select new + { + DepartmentName = department.Name, + Students = studentGroup + }; + + foreach (var v in query) + { + // Output the department's name. + Console.WriteLine($"{v.DepartmentName}:"); + + // Output each of the students in that department. + foreach (Student? student in v.Students) + { + Console.WriteLine($" {student.FirstName} {student.LastName}"); + } + } + // + } + + private static void GroupJoinMethodSyntax() + { + // + var query = departments.GroupJoin(students, department => department.ID, student => student.DepartmentID, + (department, Students) => new { DepartmentName = department.Name, Students }); + + foreach (var v in query) + { + // Output the department's name. + Console.WriteLine($"{v.DepartmentName}:"); + + // Output each of the students in that department. + foreach (Student? student in v.Students) + { + Console.WriteLine($" {student.FirstName} {student.LastName}"); + } + } + // + } + private static void GroupJoinToXmlQuerySyntax() + { + // + XElement departmentsAndStudents = new("DepartmentEnrollment", + from department in departments + join student in students on department.ID equals student.DepartmentID into studentGroup + select new XElement("Department", + new XAttribute("Name", department.Name), + from student in studentGroup + select new XElement("Student", + new XAttribute("FirstName", student.FirstName), + new XAttribute("LastName", student.LastName) + ) + ) + ); + + Console.WriteLine(departmentsAndStudents); + // + } + + private static void GroupJoinToXmlMethodSyntax() + { + // + XElement departmentsAndStudents = new("DepartmentEnrollment", + departments.GroupJoin(students, department => department.ID, student => student.DepartmentID, + (department, Students) => new XElement("Department", + new XAttribute("Name", department.Name), + from student in Students + select new XElement("Student", + new XAttribute("FirstName", student.FirstName), + new XAttribute("LastName", student.LastName) + ) + ) + ) + ); + + Console.WriteLine(departmentsAndStudents); + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupOverview.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupOverview.cs new file mode 100644 index 0000000000000..daadcf67a52ab --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupOverview.cs @@ -0,0 +1,49 @@ +namespace StandardQueryOperators; +internal class GroupOverview +{ + public static void RunAllSnippets() + { + Console.WriteLine("Group Overview"); + OverviewSampleQuery(); + Console.WriteLine("Group Overview Method"); + OverviewSampleMethod(); + } + + private static void OverviewSampleQuery() + { + // + List numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208]; + + IEnumerable> query = from number in numbers + group number by number % 2; + + foreach (var group in query) + { + Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:"); + foreach (int i in group) + { + Console.WriteLine(i); + } + } + // + } + + private static void OverviewSampleMethod() + { + // + List numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208]; + + IEnumerable> query = numbers + .GroupBy(number => number % 2); + + foreach (var group in query) + { + Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:"); + foreach (int i in group) + { + Console.WriteLine(i); + } + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.cs new file mode 100644 index 0000000000000..1c05a07a7415a --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.cs @@ -0,0 +1,262 @@ +namespace StandardQueryOperators; + +public class GroupQueryResults +{ + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Group ByProperty Results"); + GroupByPropertyQuery(); + Console.WriteLine("Group ByProperty Results Method"); + GroupByPropertyMethod(); + Console.WriteLine("Group ByValue Results"); + GroupByValueQuery(); + Console.WriteLine("Group ByValue Results Method"); + GroupByValueMethod(); + Console.WriteLine("Group ByRange Results"); + GroupByRangeQuery(); + Console.WriteLine("Group ByRange Results Method"); + GroupByRangeMethod (); + Console.WriteLine("Group ByBoolean Results"); + GroupByBooleanQuery(); + Console.WriteLine("Group ByBoolean Results Method"); + GroupByBooleanMethod(); + Console.WriteLine("Group ByCompoundKey Results"); + GroupByCompoundKeyQuery(); + Console.WriteLine("Group ByCompoundKey Results Method"); + GroupByCompoundKeyMethod(); + } + + public static void GroupByPropertyQuery() + { + // + var groupByYearQuery = + from student in students + group student by student.Year into newGroup + orderby newGroup.Key + select newGroup; + + foreach (var yearGroup in groupByYearQuery) + { + Console.WriteLine($"Key: {yearGroup.Key}"); + foreach (var student in yearGroup) + { + Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); + } + } + // + } + + public static void GroupByPropertyMethod() + { + // + // Variable groupByLastNamesQuery is an IEnumerable>. + var groupByYearQuery = students + .GroupBy(student => student.Year) + .OrderBy(newGroup => newGroup.Key); + + foreach (var yearGroup in groupByYearQuery) + { + Console.WriteLine($"Key: {yearGroup.Key}"); + foreach (var student in yearGroup) + { + Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); + } + } + // + } + + public static void GroupByValueQuery() + { + // + var groupByFirstLetterQuery = + from student in students + let firstLetter = student.LastName[0] + group student by firstLetter; + + foreach (var studentGroup in groupByFirstLetterQuery) + { + Console.WriteLine($"Key: {studentGroup.Key}"); + foreach (var student in studentGroup) + { + Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); + } + } + + // + } + + public static void GroupByValueMethod() + { + // + var groupByFirstLetterQuery = students + .GroupBy(student => student.LastName[0]); + + foreach (var studentGroup in groupByFirstLetterQuery) + { + Console.WriteLine($"Key: {studentGroup.Key}"); + foreach (var student in studentGroup) + { + Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); + } + } + // + } + + public static void GroupByRangeQuery() + { + // + static int GetPercentile(Student s) + { + double avg = s.Scores.Average(); + return avg > 0 ? (int)avg / 10 : 0; + } + + var groupByPercentileQuery = + from student in students + let percentile = GetPercentile(student) + group new + { + student.FirstName, + student.LastName + } by percentile into percentGroup + orderby percentGroup.Key + select percentGroup; + + foreach (var studentGroup in groupByPercentileQuery) + { + Console.WriteLine($"Key: {studentGroup.Key * 10}"); + foreach (var item in studentGroup) + { + Console.WriteLine($"\t{item.LastName}, {item.FirstName}"); + } + } + + // + } + + public static void GroupByRangeMethod() + { + // + static int GetPercentile(Student s) + { + double avg = s.Scores.Average(); + return avg > 0 ? (int)avg / 10 : 0; + } + + var groupByPercentileQuery = students + .Select(student => new { student, percentile = GetPercentile(student) }) + .GroupBy(student => student.percentile) + .Select(percentGroup => new + { + percentGroup.Key, + Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName }) + }) + .OrderBy(percentGroup => percentGroup.Key); + + foreach (var studentGroup in groupByPercentileQuery) + { + Console.WriteLine($"Key: {studentGroup.Key * 10}"); + foreach (var item in studentGroup.Students) + { + Console.WriteLine($"\t{item.LastName}, {item.FirstName}"); + } + } + // + } + + public static void GroupByBooleanQuery() + { + // + var groupByHighAverageQuery = + from student in students + group new + { + student.FirstName, + student.LastName + } by student.Scores.Average() > 75 into studentGroup + select studentGroup; + + foreach (var studentGroup in groupByHighAverageQuery) + { + Console.WriteLine($"Key: {studentGroup.Key}"); + foreach (var student in studentGroup) + { + Console.WriteLine($"\t{student.FirstName} {student.LastName}"); + } + } + // + } + + public static void GroupByBooleanMethod() + { + // + var groupByHighAverageQuery = students + .GroupBy(student => student.Scores.Average() > 75) + .Select(group => new + { + group.Key, + Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName }) + }); + + foreach (var studentGroup in groupByHighAverageQuery) + { + Console.WriteLine($"Key: {studentGroup.Key}"); + foreach (var student in studentGroup.Students) + { + Console.WriteLine($"\t{student.FirstName} {student.LastName}"); + } + } + // + } + + public static void GroupByCompoundKeyQuery() + { + // + var groupByCompoundKey = + from student in students + group student by new + { + FirstLetterOfLastName = student.LastName[0], + IsScoreOver85 = student.Scores[0] > 85 + } into studentGroup + orderby studentGroup.Key.FirstLetterOfLastName + select studentGroup; + + foreach (var scoreGroup in groupByCompoundKey) + { + var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85"; + Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}"); + foreach (var item in scoreGroup) + { + Console.WriteLine($"\t{item.FirstName} {item.LastName}"); + } + } + // + } + + public static void GroupByCompoundKeyMethod() + { + // + var groupByCompoundKey = students + .GroupBy(student => new + { + FirstLetterOfLastName = student.LastName[0], + IsScoreOver85 = student.Scores[0] > 85 + }) + .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName); + + foreach (var scoreGroup in groupByCompoundKey) + { + var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85"; + Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}"); + foreach (var item in scoreGroup) + { + Console.WriteLine($"\t{item.FirstName} {item.LastName}"); + } + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.cs new file mode 100644 index 0000000000000..00fd4e27b6218 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.cs @@ -0,0 +1,114 @@ +using System.Xml.Linq; + +namespace StandardQueryOperators; + +internal class IndexExamples +{ + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Sentences(); + PartsOfAQuery(); + XmlTransformation(); + } + + private static void Sentences() + { + // + string sentence = "the quick brown fox jumps over the lazy dog"; + // Split the string into individual words to create a collection. + string[] words = sentence.Split(' '); + + // Using query expression syntax. + var query = from word in words + group word.ToUpper() by word.Length into gr + orderby gr.Key + select new { Length = gr.Key, Words = gr }; + + // Using method-based query syntax. + var query2 = words. + GroupBy(w => w.Length, w => w.ToUpper()). + Select(g => new { Length = g.Key, Words = g }). + OrderBy(o => o.Length); + + foreach (var obj in query) + { + Console.WriteLine("Words of length {0}:", obj.Length); + foreach (string word in obj.Words) + Console.WriteLine(word); + } + + // This code example produces the following output: + // + // Words of length 3: + // THE + // FOX + // THE + // DOG + // Words of length 4: + // OVER + // LAZY + // Words of length 5: + // QUICK + // BROWN + // JUMPS + // + } + + private static void PartsOfAQuery() + { + // + //queryAllStudents is an IEnumerable + var queryAllStudents = from student in students + select student; + // + } + + private static void XmlTransformation() + { + // + + // Create the query. + var studentsToXML = new XElement("Root", + from student in students + let scores = string.Join(",", student.Scores) + select new XElement("student", + new XElement("First", student.FirstName), + new XElement("Last", student.LastName), + new XElement("Scores", scores) + ) // end "student" + ); // end "Root" + + // Execute the query. + Console.WriteLine(studentsToXML); + // + /* Output: + // + + + Svetlana + Omelchenko + 97,90,73,54 + + + Claire + O'Donnell + 56,78,95,95 + + ... + + Max + Lindgren + 86,88,96,63 + + + Arina + Ivanova + 93,63,70,80 + + + // + */ + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/InnerJoins.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/InnerJoins.cs new file mode 100644 index 0000000000000..55b874eeb91a8 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/InnerJoins.cs @@ -0,0 +1,230 @@ +namespace StandardQueryOperators; +public class InnerJoins +{ + private static readonly IEnumerable departments = Sources.Departments; + private static readonly IEnumerable teachers = Sources.Teachers; + private static readonly IEnumerable students = Sources.Students; + public static void RunAllSnippets() + { + Console.WriteLine("Simple Inner Join Query Syntax"); + SimpleInnerJoinQuerySyntax(); + Console.WriteLine("Simple Inner Join Method Syntax"); + SimpleInnerJoinMethodSyntax(); + Console.WriteLine("Composite Key Query Syntax"); + CompositeKeyQuerySyntax(); + Console.WriteLine("Composite Key Method Syntax"); + CompositeKeyMethodSyntax(); + Console.WriteLine("Multiple Join Query Syntax"); + MultipleJoinQuerySyntax(); + Console.WriteLine("Multiple Join Method Syntax"); + MultipleJoinMethodSyntax(); + Console.WriteLine("Inner Group Join Query Syntax"); + InnerGroupJoinQuerySyntax(); + Console.WriteLine("Inner Join Query Syntax"); + InnerJoinQuerySyntax(); + Console.WriteLine("Inner Group Join Method Syntax"); + InnerGroupJoinMethodSyntax(); + Console.WriteLine("Inner Join Method Syntax"); + InnerJoinMethodSyntax(); + } + + private static void SimpleInnerJoinQuerySyntax() + { + // + var query = from department in departments + join teacher in teachers on department.TeacherID equals teacher.ID + select new + { + DepartmentName = department.Name, + TeacherName = $"{teacher.First} {teacher.Last}" + }; + + foreach (var departmentAndTeacher in query) + { + Console.WriteLine($"{departmentAndTeacher.DepartmentName} is managed by {departmentAndTeacher.TeacherName}"); + } + // + } + + private static void SimpleInnerJoinMethodSyntax() + { + // + var query = teachers + .Join(departments, teacher => teacher.ID, department => department.TeacherID, + (teacher, department) => + new { DepartmentName = department.Name, TeacherName = $"{teacher.First} {teacher.Last}" }); + + foreach (var departmentAndTeacher in query) + { + Console.WriteLine($"{departmentAndTeacher.DepartmentName} is managed by {departmentAndTeacher.TeacherName}"); + } + // + } + + private static void CompositeKeyQuerySyntax() + { + // + // Join the two data sources based on a composite key consisting of first and last name, + // to determine which employees are also students. + IEnumerable query = + from teacher in teachers + join student in students on new + { + FirstName = teacher.First, + LastName = teacher.Last + } equals new + { + student.FirstName, + student.LastName + } + select teacher.First + " " + teacher.Last; + + string result = "The following people are both teachers and students:\r\n"; + foreach (string name in query) + { + result += $"{name}\r\n"; + } + Console.Write(result); + // + + } + + private static void CompositeKeyMethodSyntax() + { + // + IEnumerable query = teachers + .Join(students, + teacher => new { FirstName = teacher.First, LastName = teacher.Last }, + student => new { student.FirstName, student.LastName }, + (teacher, student) => $"{teacher.First} {teacher.Last}" + ); + + Console.WriteLine("The following people are both teachers and students:"); + foreach (string name in query) + { + Console.WriteLine(name); + } + // + } + + private static void MultipleJoinQuerySyntax() + { + // + // The first join matches Department.ID and Student.DepartmentID from the list of students and + // departments, based on a common ID. The second join matches teachers who lead departments + // with the students studying in that department. + var query = from student in students + join department in departments on student.DepartmentID equals department.ID + join teacher in teachers on department.TeacherID equals teacher.ID + select new { + StudentName = $"{student.FirstName} {student.LastName}", + DepartmentName = department.Name, + TeacherName = $"{teacher.First} {teacher.Last}" + }; + + foreach (var obj in query) + { + Console.WriteLine($"""The student "{obj.StudentName}" studies in the department run by "{obj.TeacherName}"."""); + } + // + } + + private static void MultipleJoinMethodSyntax() + { + // + var query = students + .Join(departments, student => student.DepartmentID, department => department.ID, + (student, department) => new { student, department }) + .Join(teachers, commonDepartment => commonDepartment.department.TeacherID, teacher => teacher.ID, + (commonDepartment, teacher) => new + { + StudentName = $"{commonDepartment.student.FirstName} {commonDepartment.student.LastName}", + DepartmentName = commonDepartment.department.Name, + TeacherName = $"{teacher.First} {teacher.Last}" + }); + + foreach (var obj in query) + { + Console.WriteLine($"""The student "{obj.StudentName}" studies in the department run by "{obj.TeacherName}"."""); + } + // + } + + private static void InnerGroupJoinQuerySyntax() + { + // + var query1 = + from department in departments + join student in students on department.ID equals student.DepartmentID into gj + from subStudent in gj + select new + { + DepartmentName = department.Name, + StudentName = $"{subStudent.FirstName} {subStudent.LastName}" + }; + Console.WriteLine("Inner join using GroupJoin():"); + foreach (var v in query1) + { + Console.WriteLine($"{v.DepartmentName} - {v.StudentName}"); + } + // + } + + private static void InnerJoinQuerySyntax() + { + // + var query2 = from department in departments + join student in students on department.ID equals student.DepartmentID + select new + { + DepartmentName = department.Name, + StudentName = $"{student.FirstName} {student.LastName}" + }; + + Console.WriteLine("The equivalent operation using Join():"); + foreach (var v in query2) + { + Console.WriteLine($"{v.DepartmentName} - {v.StudentName}"); + } + // + } + + private static void InnerGroupJoinMethodSyntax() + { + // + var queryMethod1 = departments + .GroupJoin(students, department => department.ID, student => student.DepartmentID, + (department, gj) => new { department, gj }) + .SelectMany(departmentAndStudent => departmentAndStudent.gj, + (departmentAndStudent, subStudent) => new + { + DepartmentName = departmentAndStudent.department.Name, + StudentName = $"{subStudent.FirstName} {subStudent.LastName}" + }); + + Console.WriteLine("Inner join using GroupJoin():"); + foreach (var v in queryMethod1) + { + Console.WriteLine($"{v.DepartmentName} - {v.StudentName}"); + } + // + } + + private static void InnerJoinMethodSyntax() + { + // + var queryMethod2 = departments.Join(students, departments => departments.ID, student => student.DepartmentID, + (department, student) => new + { + DepartmentName = department.Name, + StudentName = $"{student.FirstName} {student.LastName}" + }); + + Console.WriteLine("The equivalent operation using Join():"); + foreach (var v in queryMethod2) + { + Console.WriteLine($"{v.DepartmentName} - {v.StudentName}"); + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/JoinOverviewExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/JoinOverviewExamples.cs new file mode 100644 index 0000000000000..aadf8f430ad8a --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/JoinOverviewExamples.cs @@ -0,0 +1,82 @@ +namespace StandardQueryOperators; +public class JoinOverviewExamples +{ + private static readonly IEnumerable students = Sources.Students; + private static readonly IEnumerable departments = Sources.Departments; + + public static void RunAllSnippets() + { + Console.WriteLine("Join Example"); + JoinExampleQuery(); + Console.WriteLine("Join Example Method"); + JoinExampleMethod(); + Console.WriteLine("Group Join Example"); + GroupJoinExampleQuery(); + Console.WriteLine("Group Join Example Method"); + GroupJoinExampleMethod(); + } + + private static void JoinExampleQuery() + { + // + var query = from student in students + join department in departments on student.DepartmentID equals department.ID + select new { Name = $"{student.FirstName} {student.LastName}", DepartmentName = department.Name }; + + foreach (var item in query) + { + Console.WriteLine($"{item.Name} - {item.DepartmentName}"); + } + // + } + + private static void JoinExampleMethod() + { + // + var query = students.Join(departments, + student => student.DepartmentID, department => department.ID, + (student, department) => new { Name = $"{student.FirstName} {student.LastName}", DepartmentName = department.Name }); + + foreach (var item in query) + { + Console.WriteLine($"{item.Name} - {item.DepartmentName}"); + } + // + } + private static void GroupJoinExampleQuery() + { + // + IEnumerable> studentGroups = from department in departments + join student in students on department.ID equals student.DepartmentID into studentGroup + select studentGroup; + + foreach (IEnumerable studentGroup in studentGroups) + { + Console.WriteLine("Group"); + foreach (Student student in studentGroup) + { + Console.WriteLine($" - {student.FirstName}, {student.LastName}"); + } + } + // + } + + private static void GroupJoinExampleMethod() + { + // + // Join department and student based on DepartmentId and grouping result + IEnumerable> studentGroups = departments.GroupJoin(students, + department => department.ID, student => student.DepartmentID, + (department, studentGroup) => studentGroup); + + foreach (IEnumerable studentGroup in studentGroups) + { + Console.WriteLine("Group"); + foreach (Student student in studentGroup) + { + Console.WriteLine($" - {student.FirstName}, {student.LastName}"); + } + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/LeftOuterJoins.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/LeftOuterJoins.cs new file mode 100644 index 0000000000000..58dfbe6c68669 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/LeftOuterJoins.cs @@ -0,0 +1,54 @@ +namespace StandardQueryOperators; +public class LeftOuterJoins +{ + private static readonly IEnumerable departments = Sources.Departments; + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Left Outer Join Query Syntax"); + LeftOuterJoinQuerySyntax(); + Console.WriteLine("Left Outer Join Method Syntax"); + LeftOuterJoinMethodSyntax(); + } + + private static void LeftOuterJoinQuerySyntax() + { + // + var query = + from student in students + join department in departments on student.DepartmentID equals department.ID into gj + from subgroup in gj.DefaultIfEmpty() + select new + { + student.FirstName, + student.LastName, + Department = subgroup?.Name ?? string.Empty + }; + + foreach (var v in query) + { + Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}"); + } + // + } + + private static void LeftOuterJoinMethodSyntax() + { + // + var query = students.GroupJoin(departments, student => student.DepartmentID, department => department.ID, + (student, department) => new { student, subgroup = department.DefaultIfEmpty() }) + .Select(gj => new + { + gj.student.FirstName, + gj.student.LastName, + Department = gj.subgroup?.FirstOrDefault()?.Name ?? string.Empty + }); + + foreach (var v in query) + { + Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}"); + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/NestedGroups.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/NestedGroups.cs new file mode 100644 index 0000000000000..55811e461dd0f --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/NestedGroups.cs @@ -0,0 +1,67 @@ +namespace StandardQueryOperators; + +public static class NestedGroups +{ + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Nested Group Query Syntax"); + NestedGroupQuery(); + Console.WriteLine("Nested Group Method Syntax"); + NestedGroupMethod(); + } + + private static void NestedGroupQuery() + { + // + var nestedGroupsQuery = + from student in students + group student by student.Year into newGroup1 + from newGroup2 in + from student in newGroup1 + group student by student.LastName + group newGroup2 by newGroup1.Key; + + foreach (var outerGroup in nestedGroupsQuery) + { + Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}"); + foreach (var innerGroup in outerGroup) + { + Console.WriteLine($"\tNames that begin with: {innerGroup.Key}"); + foreach (var innerGroupElement in innerGroup) + { + Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}"); + } + } + } + // + } + private static void NestedGroupMethod() + { + // + var nestedGroupsQuery = + students + .GroupBy(student => student.Year) + .Select(newGroup1 => new + { + newGroup1.Key, + NestedGroup = newGroup1 + .GroupBy(student => student.LastName) + }); + + foreach (var outerGroup in nestedGroupsQuery) + { + Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}"); + foreach (var innerGroup in outerGroup.NestedGroup) + { + Console.WriteLine($"\tNames that begin with: {innerGroup.Key}"); + foreach (var innerGroupElement in innerGroup) + { + Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}"); + } + } + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/OrderResultsOfJoin.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/OrderResultsOfJoin.cs new file mode 100644 index 0000000000000..0d1a71030de46 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/OrderResultsOfJoin.cs @@ -0,0 +1,122 @@ +namespace StandardQueryOperators; + +public class OrderResultsOfJoin +{ + private static readonly IEnumerable departments = Sources.Departments; + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Order Results of Join"); + OrderResultsOfJoinQuerySyntax(); + Console.WriteLine("Order Results of Join Method"); + OrderResultsOfJoinMethodSyntax(); + } + + private static void OrderResultsOfJoinQuerySyntax() + { + // + var orderedQuery = from department in departments + join student in students on department.ID equals student.DepartmentID into studentGroup + orderby department.Name + select new + { + DepartmentName = department.Name, + Students = from student in studentGroup + orderby student.LastName + select student + }; + + foreach (var departmentList in orderedQuery) + { + Console.WriteLine(departmentList.DepartmentName); + foreach (var student in departmentList.Students) + { + Console.WriteLine($" {student.LastName,-10} {student.FirstName,-10}"); + } + } + /* Output: + Chemistry + Balzan Josephine + Fakhouri Fadi + Popov Innocenty + Seleznyova Sofiya + Vella Carmen + Economics + Adams Terry + Adaobi Izuchukwu + Berggren Jeanette + Garcia Cesar + Ifeoma Nwanneka + Jamuike Ifeanacho + Larsson Naima + Svensson Noel + Ugomma Ifunanya + Engineering + Axelsson Erik + Berg Veronika + Engström Nancy + Hicks Cassie + Keever Bruce + Micallef Nicholas + Mortensen Sven + Nilsson Erna + Tucker Michael + Yermolayeva Anna + English + Andersson Sarah + Feng Hanying + Ivanova Arina + Jakobsson Jesper + Jensen Christiane + Johansson Mark + Kolpakova Nadezhda + Omelchenko Svetlana + Urquhart Donald + Mathematics + Frost Gaby + Garcia Hugo + Hedlund Anna + Kovaleva Katerina + Lindgren Max + Maslova Evgeniya + Olsson Ruth + Sammut Maria + Sazonova Anastasiya + Physics + Åkesson Sami + Edwards Amy E. + Falzon John + Garcia Debra + Hansson Sanna + Mattsson Martina + Richardson Don + Zabokritski Eugene + */ + // + } + + private static void OrderResultsOfJoinMethodSyntax() + { + // + var orderedQuery = departments + .GroupJoin(students, department => department.ID, student => student.DepartmentID, + (department, studentGroup) => new + { + DepartmentName = department.Name, + Students = studentGroup.OrderBy(student => student.LastName) + }) + .OrderBy(department => department.DepartmentName); + + + foreach (var departmentList in orderedQuery) + { + Console.WriteLine(departmentList.DepartmentName); + foreach (var student in departmentList.Students) + { + Console.WriteLine($" {student.LastName,-10} {student.FirstName,-10}"); + } + } + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/PartitionExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/PartitionExamples.cs new file mode 100644 index 0000000000000..443c6c182cf87 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/PartitionExamples.cs @@ -0,0 +1,111 @@ + +namespace StandardQueryOperators; +public class PartitionExamples +{ + public static void RunAllSnippets() + { + Console.WriteLine("Take"); + TakeExample(); + Console.WriteLine("Skip"); + SkipExample(); + Console.WriteLine("TakeWhile"); + TakeWhileExample(); + Console.WriteLine("SkipWhile"); + SkipWhileExample(); + Console.WriteLine("Chuck"); + ChuckExample(); + } + + private static void TakeExample() + { + // + foreach (int number in Enumerable.Range(0, 8).Take(3)) + { + Console.WriteLine(number); + } + // This code produces the following output: + // 0 + // 1 + // 2 + // + } + + private static void SkipExample() + { + // + foreach (int number in Enumerable.Range(0, 8).Skip(3)) + { + Console.WriteLine(number); + } + // This code produces the following output: + // 3 + // 4 + // 5 + // 6 + // 7 + // + } + + + private static void TakeWhileExample() + { + // + foreach (int number in Enumerable.Range(0, 8).TakeWhile(n => n < 5)) + { + Console.WriteLine(number); + } + // This code produces the following output: + // 0 + // 1 + // 2 + // 3 + // 4 + // + } + + + private static void SkipWhileExample() + { + // + foreach (int number in Enumerable.Range(0, 8).SkipWhile(n => n < 5)) + { + Console.WriteLine(number); + } + // This code produces the following output: + // 5 + // 6 + // 7 + // + } + +private static void ChuckExample() + { + // + int chunkNumber = 1; + foreach (int[] chunk in Enumerable.Range(0, 8).Chunk(3)) + { + Console.WriteLine($"Chunk {chunkNumber++}:"); + foreach (int item in chunk) + { + Console.WriteLine($" {item}"); + } + + Console.WriteLine(); + } + // This code produces the following output: + // Chunk 1: + // 0 + // 1 + // 2 + // + //Chunk 2: + // 3 + // 4 + // 5 + // + //Chunk 3: + // 6 + // 7 + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/Program.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/Program.cs new file mode 100644 index 0000000000000..61845ba964a6b --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/Program.cs @@ -0,0 +1,47 @@ +// Main program to run all the samples for the +// standard query operators section of the LINQ learning series. + +using StandardQueryOperators; + +Console.WriteLine("========== Index snippets =========="); +IndexExamples.RunAllSnippets(); +Console.WriteLine("========== Order Join results =========="); +OrderResultsOfJoin.RunAllSnippets(); + +Console.WriteLine("========== Where filter =========="); +WhereFilter.RunAllSnippets(); +Console.WriteLine("========== Select projection =========="); +SelectProjectionExamples.RunAllSnippets(); + +Console.WriteLine("========== Set operations =========="); +SetOperations.RunAllSnippets(); + +Console.WriteLine("========== Sort Examples =========="); +SortExamples.RunAllSnippets(); + +Console.WriteLine("========== Quantifier Examles =========="); +QuantifierExamples.RunAllSnippets(); + +Console.WriteLine("========== Partitionss =========="); +PartitionExamples.RunAllSnippets(); + +Console.WriteLine("========== Conversions =========="); +ConversionExamples.RunAllSnippets(); + +Console.WriteLine("========== Join Overview =========="); +JoinOverviewExamples.RunAllSnippets(); +Console.WriteLine("========== Inner Join =========="); +InnerJoins.RunAllSnippets(); +Console.WriteLine("========== Group Join =========="); +GroupJoins.RunAllSnippets(); +Console.WriteLine("========== Left Outer Joins =========="); +LeftOuterJoins.RunAllSnippets(); + +Console.WriteLine("========== Grouping overview =========="); +GroupOverview.RunAllSnippets(); +Console.WriteLine("========== Group Query =========="); +GroupQueryResults.RunAllSnippets(); +Console.WriteLine("========== Nested Groups =========="); +NestedGroups.RunAllSnippets(); +Console.WriteLine("========== Subquery on Group =========="); +SubqueryOnGroup.RunAllSnippets(); diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/QuantifierExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/QuantifierExamples.cs new file mode 100644 index 0000000000000..efff37edde3c9 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/QuantifierExamples.cs @@ -0,0 +1,89 @@ +using System.Diagnostics; +using System.Security.Claims; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace StandardQueryOperators; +public class QuantifierExamples +{ + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("All:"); + AllExample(); + Console.WriteLine("Any:"); + AnyExample(); + Console.WriteLine("Contains:"); + ContainsExample(); + } + + private static void AllExample() + { + // + IEnumerable names = from student in students + where student.Scores.All(score => score > 70) + select $"{student.FirstName} {student.LastName}: {string.Join(", ", student.Scores.Select(s => s.ToString()))}"; + + foreach (string name in names) + { + Console.WriteLine($"{name}"); + } + + // This code produces the following output: + // + // Cesar Garcia: 71, 86, 77, 97 + // Nancy Engström: 75, 73, 78, 83 + // Ifunanya Ugomma: 84, 82, 96, 80 + // + } + + private static void AnyExample() + { + // + + IEnumerable names = from student in students + where student.Scores.Any(score => score > 95) + select $"{student.FirstName} {student.LastName}: {student.Scores.Max()}"; + + foreach (string name in names) + { + Console.WriteLine($"{name}"); + } + + // This code produces the following output: + // + // Svetlana Omelchenko: 97 + // Cesar Garcia: 97 + // Debra Garcia: 96 + // Ifeanacho Jamuike: 98 + // Ifunanya Ugomma: 96 + // Michelle Caruana: 97 + // Nwanneka Ifeoma: 98 + // Martina Mattsson: 96 + // Anastasiya Sazonova: 96 + // Jesper Jakobsson: 98 + // Max Lindgren: 96 + // + } + + private static void ContainsExample() + { + // + + // Determine which market contains fruit names equal 'kiwi' + IEnumerable names = from student in students + where student.Scores.Contains(95) + select $"{student.FirstName} {student.LastName}: {string.Join(", ", student.Scores.Select(s => s.ToString()))}"; + + foreach (string name in names) + { + Console.WriteLine($"{name}"); + } + + // This code produces the following output: + // + // Claire O'Donnell: 56, 78, 95, 95 + // Donald Urquhart: 92, 90, 95, 57 + // + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SelectProjectionExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SelectProjectionExamples.cs new file mode 100644 index 0000000000000..f474cc10f34a0 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SelectProjectionExamples.cs @@ -0,0 +1,265 @@ +using System.Text; + +namespace StandardQueryOperators; +internal class SelectProjectionExamples +{ + public static void RunAllSnippets() + { + SelectSimpleQuerySyntax(); + SelectSimpleMethodSyntax(); + SelectManyQuerySyntax(); + SelectManyMethodSyntax(); + NumbersAndLetters(); + SelectVsSelectMany(); + } + + private static void SelectSimpleQuerySyntax() + { + // + List words = ["an", "apple", "a", "day"]; + + var query = from word in words + select word.Substring(0, 1); + + foreach (string s in query) + { + Console.WriteLine(s); + } + + /* This code produces the following output: + + a + a + a + d + */ + // + } + + private static void SelectSimpleMethodSyntax() + { + // + List words = ["an", "apple", "a", "day"]; + + var query = words.Select(word => word.Substring(0, 1)); + + foreach (string s in query) + { + Console.WriteLine(s); + } + + /* This code produces the following output: + + a + a + a + d + */ + // + } + private static void SelectManyQuerySyntax() + { + // + List phrases = ["an apple a day", "the quick brown fox"]; + + var query = from phrase in phrases + from word in phrase.Split(' ') + select word; + + foreach (string s in query) + { + Console.WriteLine(s); + } + + /* This code produces the following output: + + an + apple + a + day + the + quick + brown + fox + */ + // + } + + private static void SelectManyMethodSyntax() + { + // + List phrases = ["an apple a day", "the quick brown fox"]; + + var query = phrases.SelectMany(phrases => phrases.Split(' ')); + + foreach (string s in query) + { + Console.WriteLine(s); + } + + /* This code produces the following output: + + an + apple + a + day + the + quick + brown + fox + */ + // + } + private static void NumbersAndLetters() + { + Console.OutputEncoding = Encoding.UTF8; + + // + // An int array with 7 elements. + IEnumerable numbers = [1, 2, 3, 4, 5, 6, 7]; + // A char array with 6 elements. + IEnumerable letters = ['A', 'B', 'C', 'D', 'E', 'F']; + // + + // + // A string array with 8 elements. + IEnumerable emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"]; + // + + // + var query = from number in numbers + from letter in letters + select (number, letter); + + foreach (var item in query) + { + Console.WriteLine(item); + } + // + + // + var method = numbers + .SelectMany(number => letters, + (number, letter) => (number, letter)); + + foreach (var item in method) + { + Console.WriteLine(item); + } + // + + ZipTupleExample(numbers, letters); + ZipTripleExample(numbers, letters, emoji); + ZipResultExample(numbers, letters); + } + + private static void ZipTupleExample( + IEnumerable numbers, + IEnumerable letters) + { + Console.WriteLine("Zip (TFirst, TSecond):"); + + // + foreach ((int number, char letter) in numbers.Zip(letters)) + { + Console.WriteLine($"Number: {number} zipped with letter: '{letter}'"); + } + // This code produces the following output: + // Number: 1 zipped with letter: 'A' + // Number: 2 zipped with letter: 'B' + // Number: 3 zipped with letter: 'C' + // Number: 4 zipped with letter: 'D' + // Number: 5 zipped with letter: 'E' + // Number: 6 zipped with letter: 'F' + // + + Console.WriteLine(); + } + + private static void ZipResultExample( + IEnumerable numbers, + IEnumerable letters) + { + Console.WriteLine("Zip (TFirst, TSecond, Func):"); + + // + foreach (string result in + numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})")) + { + Console.WriteLine(result); + } + // This code produces the following output: + // 1 = A (65) + // 2 = B (66) + // 3 = C (67) + // 4 = D (68) + // 5 = E (69) + // 6 = F (70) + // + + Console.WriteLine(); + } + + private static void ZipTripleExample( + IEnumerable numbers, + IEnumerable letters, + IEnumerable emoji) + { + Console.WriteLine("Zip (TFirst, TSecond, TThird):"); + + // + foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji)) + { + Console.WriteLine( + $"Number: {number} is zipped with letter: '{letter}' and emoji: {em}"); + } + // This code produces the following output: + // Number: 1 is zipped with letter: 'A' and emoji: 🤓 + // Number: 2 is zipped with letter: 'B' and emoji: 🔥 + // Number: 3 is zipped with letter: 'C' and emoji: 🎉 + // Number: 4 is zipped with letter: 'D' and emoji: 👀 + // Number: 5 is zipped with letter: 'E' and emoji: ⭐ + // Number: 6 is zipped with letter: 'F' and emoji: 💜 + // + + Console.WriteLine(); + } + + // + class Bouquet + { + public required List Flowers { get; init; } + } + + static void SelectVsSelectMany() + { + List bouquets = + [ + new Bouquet { Flowers = ["sunflower", "daisy", "daffodil", "larkspur"] }, + new Bouquet { Flowers = ["tulip", "rose", "orchid"] }, + new Bouquet { Flowers = ["gladiolis", "lily", "snapdragon", "aster", "protea"] }, + new Bouquet { Flowers = ["larkspur", "lilac", "iris", "dahlia"] } + ]; + + IEnumerable> query1 = bouquets.Select(bq => bq.Flowers); + + IEnumerable query2 = bouquets.SelectMany(bq => bq.Flowers); + + Console.WriteLine("Results by using Select():"); + // Note the extra foreach loop here. + foreach (IEnumerable collection in query1) + { + foreach (string item in collection) + { + Console.WriteLine(item); + } + } + + Console.WriteLine("\nResults by using SelectMany():"); + foreach (string item in query2) + { + Console.WriteLine(item); + } + } + // +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SetOperations.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SetOperations.cs new file mode 100644 index 0000000000000..c71bcb7fdc419 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SetOperations.cs @@ -0,0 +1,210 @@ +namespace StandardQueryOperators; +internal class SetOperations +{ + private static readonly IEnumerable teachers = Sources.Teachers; + private static readonly IEnumerable students = Sources.Students; + public static void RunAllSnippets() + { + Console.WriteLine("Distinct"); + Distinct(); + Console.WriteLine("DistinctBy"); + DistinctByExample(); + Console.WriteLine("Except"); + Except(); + Console.WriteLine("ExceptBy"); + ExceptByExample(); + Console.WriteLine("Intersect"); + IntersectInteractive(); + Console.WriteLine("IntersectBy"); + IntersectByExample(); + Console.WriteLine("Union"); + UnionInteractive(); + Console.WriteLine("UnionBy"); + UnionByExample(); + } + + private static void Distinct() + { + // + string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"]; + + IEnumerable query = from word in words.Distinct() + select word; + + foreach (var str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + * + * the + * quick + * brown + * fox + * jumped + * over + * lazy + * dog + */ + // + } + + private static void Except() + { + // + string[] words1 = ["the", "quick", "brown", "fox"]; + string[] words2 = ["jumped", "over", "the", "lazy", "dog"]; + + IEnumerable query = from word in words1.Except(words2) + select word; + + foreach (var str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + * + * quick + * brown + * fox + */ + // + } + + + private static void DistinctByExample() + { + Console.WriteLine("DistinctBy:"); + + // + string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"]; + + foreach (string word in words.DistinctBy(p => p.Length)) + { + Console.WriteLine(word); + } + + // This code produces the following output: + // the + // quick + // jumped + // over + // + + Console.WriteLine(); + } + + private static void ExceptByExample() + { + Console.WriteLine("ExceptBy:"); + + // Teachers except department chairs. + + // + int[] teachersToExclude = + [ + 901, // English + 965, // Mathematics + 932, // Engineering + 945, // Economics + 987, // Physics + 901 // Chemistry + ]; + + foreach (Teacher teacher in + teachers.ExceptBy( + teachersToExclude, teacher => teacher.ID)) + { + Console.WriteLine($"{teacher.First} {teacher.Last}"); + } + // + + Console.WriteLine(); + } + + private static void IntersectInteractive() + { + // + string[] words1 = ["the", "quick", "brown", "fox"]; + string[] words2 = ["jumped", "over", "the", "lazy", "dog"]; + + IEnumerable query = from word in words1.Intersect(words2) + select word; + + foreach (var str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + * + * the + */ + // + } + + internal static void IntersectByExample() + { + Console.WriteLine("IntersectBy:"); + + // Students who are also teachers. + + // + foreach (Student person in + students.IntersectBy( + teachers.Select(t => (t.First, t.Last)), s => (s.FirstName, s.LastName))) + { + Console.WriteLine($"{person.FirstName} {person.LastName}"); + } + // + + Console.WriteLine(); + } + + private static void UnionInteractive() + { + // + string[] words1 = ["the", "quick", "brown", "fox"]; + string[] words2 = ["jumped", "over", "the", "lazy", "dog"]; + + IEnumerable query = from word in words1.Union(words2) + select word; + + foreach (var str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + * + * the + * quick + * brown + * fox + * jumped + * over + * lazy + * dog + */ + // + } + + private static void UnionByExample() + { + Console.WriteLine("UnionBy:"); + + // All teachers and students + // + foreach (var person in + students.Select(s => (s.FirstName, s.LastName)).UnionBy( + teachers.Select(t => (FirstName: t.First, LastName: t.Last)), s => (s.FirstName, s.LastName))) + { + Console.WriteLine($"{person.FirstName} {person.LastName}"); + } + // + + Console.WriteLine(); + } +} diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SortExamples.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SortExamples.cs new file mode 100644 index 0000000000000..3df03d1612a85 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SortExamples.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; + +namespace StandardQueryOperators; + +internal class SortExamples +{ + private static readonly IEnumerable teachers = Sources.Teachers; + public static void RunAllSnippets() + { + Console.WriteLine("Ascending Query"); + AscendingSortQuerySyntax(); + Console.WriteLine("Ascending Method"); + AscendingSortMethodSyntax(); + Console.WriteLine("Descending Query"); + DescendingSortQuerySyntax(); + Console.WriteLine("Descending Method"); + DescendingSortMethodSyntax(); + Console.WriteLine("Ascending Secondary Query"); + SecondaryAscendingSortQuerySyntax(); + Console.WriteLine("Ascending Secondary Method"); + SecondaryAscendingSortMethodSyntax(); + Console.WriteLine("Descending Secondary Query"); + SecondaryDescendingSortQuerySyntax(); + Console.WriteLine("Descending Secondary Method"); + SecondaryDescendingSortMethodSyntax(); + } + + private static void AscendingSortQuerySyntax() + { + // + IEnumerable query = from teacher in teachers + orderby teacher.Last + select teacher.Last; + + foreach (string str in query) + { + Console.WriteLine(str); + } + // + } + + private static void AscendingSortMethodSyntax() + { + // + IEnumerable query = teachers + .OrderBy(teacher => teacher.Last) + .Select(teacher => teacher.Last); + + foreach (string str in query) + { + Console.WriteLine(str); + } + // + } + + private static void DescendingSortQuerySyntax() + { + // + IEnumerable query = from teacher in teachers + orderby teacher.Last descending + select teacher.Last; + + foreach (string str in query) + { + Console.WriteLine(str); + } + // + } + + private static void DescendingSortMethodSyntax() + { + // + IEnumerable query = teachers + .OrderByDescending(teacher => teacher.Last) + .Select(teacher => teacher.Last); + + foreach (string str in query) + { + Console.WriteLine(str); + } + // + } + + private static void SecondaryAscendingSortQuerySyntax() + { + // + IEnumerable<(string, string)> query = from teacher in teachers + orderby teacher.City, teacher.Last + select (teacher.Last, teacher.City); + + foreach ((string last, string city) in query) + { + Console.WriteLine($"City: {city}, Last Name: {last}"); + } + // + } + + private static void SecondaryAscendingSortMethodSyntax() + { + // + IEnumerable<(string, string)> query = teachers + .OrderBy(teacher => teacher.City) + .ThenBy(teacher => teacher.Last) + .Select(teacher => (teacher.Last, teacher.City)); + + foreach ((string last, string city) in query) + { + Console.WriteLine($"City: {city}, Last Name: {last}"); + } + // + } + + private static void SecondaryDescendingSortQuerySyntax() + { + // + IEnumerable<(string, string)> query = from teacher in teachers + orderby teacher.City, teacher.Last descending + select (teacher.Last, teacher.City); + + foreach ((string last, string city) in query) + { + Console.WriteLine($"City: {city}, Last Name: {last}"); + } + // + } + private static void SecondaryDescendingSortMethodSyntax() + { + // + IEnumerable<(string, string)> query = teachers + .OrderBy(teacher => teacher.City) + .ThenByDescending(teacher => teacher.Last) + .Select(teacher => (teacher.Last, teacher.City)); + + foreach ((string last, string city) in query) + { + Console.WriteLine($"City: {city}, Last Name: {last}"); + } + // + } +} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/SubqueryOnGroup.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SubqueryOnGroup.cs similarity index 62% rename from samples/snippets/csharp/concepts/linq/LinqSamples/SubqueryOnGroup.cs rename to docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SubqueryOnGroup.cs index 2e2b36c208bff..8976474138ba4 100644 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/SubqueryOnGroup.cs +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SubqueryOnGroup.cs @@ -1,12 +1,20 @@ -using static LinqSamples.Student; +namespace StandardQueryOperators; -namespace LinqSamples; - -public static class SubqueryOnGroup +public class SubqueryOnGroup { - public static void SubqueryOnGroup1() + private static readonly IEnumerable students = Sources.Students; + + public static void RunAllSnippets() + { + Console.WriteLine("Subquery on Group"); + SubqueryOnGroupQuery(); + Console.WriteLine("Subquery on Group Method"); + SubqueryOnGroupMethod(); + } + + private static void SubqueryOnGroupQuery() { - // + // var queryGroupMax = from student in students group student by student.Year into studentGroup @@ -15,7 +23,7 @@ group student by student.Year into studentGroup Level = studentGroup.Key, HighestScore = ( from student2 in studentGroup - select student2.ExamScores.Average() + select student2.Scores.Average() ).Max() }; @@ -27,19 +35,19 @@ select student2.ExamScores.Average() Console.WriteLine($" {item.Level} Highest Score={item.HighestScore}"); } - // + // } - public static void SubqueryOnGroup2() + public static void SubqueryOnGroupMethod() { - // + // var queryGroupMax = students .GroupBy(student => student.Year) .Select(studentGroup => new { Level = studentGroup.Key, - HighestScore = studentGroup.Max(student2 => student2.ExamScores.Average()) + HighestScore = studentGroup.Max(student2 => student2.Scores.Average()) }); var count = queryGroupMax.Count(); @@ -49,6 +57,6 @@ public static void SubqueryOnGroup2() { Console.WriteLine($" {item.Level} Highest Score={item.HighestScore}"); } - // + // } } diff --git a/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/WhereFilter.cs b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/WhereFilter.cs new file mode 100644 index 0000000000000..e0d5d481c2a79 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/WhereFilter.cs @@ -0,0 +1,52 @@ +namespace StandardQueryOperators; +public class WhereFilter +{ + public static void RunAllSnippets() + { + WhereSampleQuerySyntax(); + WhereSampleMethodSyntax(); + } + + private static void WhereSampleQuerySyntax() + { + // + string[] words = ["the", "quick", "brown", "fox", "jumps"]; + + IEnumerable query = from word in words + where word.Length == 3 + select word; + + foreach (string str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + + the + fox + */ + // + } + + private static void WhereSampleMethodSyntax() + { + // + string[] words = ["the", "quick", "brown", "fox", "jumps"]; + + IEnumerable query = + words.Where(word => word.Length == 3); + + foreach (string str in query) + { + Console.WriteLine(str); + } + + /* This code produces the following output: + + the + fox + */ + // + } +} diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/Sandbox.csproj b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/standard-query-operators.csproj similarity index 68% rename from samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/Sandbox.csproj rename to docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/standard-query-operators.csproj index f704bf4988fa6..a704a05b7ff6b 100644 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/Sandbox.csproj +++ b/docs/csharp/linq/standard-query-operators/snippets/standard-query-operators/standard-query-operators.csproj @@ -1,10 +1,11 @@ - + Exe net8.0 - enable + StandardQueryOperators enable + enable diff --git a/docs/csharp/linq/standard-query-operators/sorting-data.md b/docs/csharp/linq/standard-query-operators/sorting-data.md new file mode 100644 index 0000000000000..24bbfe8d1fa39 --- /dev/null +++ b/docs/csharp/linq/standard-query-operators/sorting-data.md @@ -0,0 +1,76 @@ +--- +title: "Sorting Data (C#)" +description: Learn about sort operations and the standard query operator methods that perform sort operations in LINQ in C#. +ms.date: 01/22/2024 +--- +# Sorting Data (C#) + +A sorting operation orders the elements of a sequence based on one or more attributes. The first sort criterion performs a primary sort on the elements. By specifying a second sort criterion, you can sort the elements within each primary sort group. + +The following illustration shows the results of an alphabetical sort operation on a sequence of characters: + +:::image type="content" source="./media/sorting-data/alphabetical-sort-operation.png" alt-text="Graphic that shows an alphabetical sort operation."::: + +The standard query operator methods that sort data are listed in the following section. + +## Methods + +|Method Name|Description|C# Query Expression Syntax|More Information| +|-----------------|-----------------|---------------------------------|----------------------| +|OrderBy|Sorts values in ascending order.|`orderby`|

| +|OrderByDescending|Sorts values in descending order.|`orderby … descending`|

| +|ThenBy|Performs a secondary sort in ascending order.|`orderby …, …`|

| +|ThenByDescending|Performs a secondary sort in descending order.|`orderby …, … descending`|

| +|Reverse|Reverses the order of the elements in a collection.|Not applicable.|

| + +The following examples in this article use the common data sources for this area: + +:::code language="csharp" source="./snippets/standard-query-operators/DataSources.cs" id="QueryDataSource"::: + +Each `Student` has a grade level, a primary department, and a series of scores. A `Teacher` also has a `City` property that identifies the campus where the teacher holds classes. A `Department` has a name, and a reference to a `Teacher` who serves as the department head. + +## Primary Ascending Sort + +The following example demonstrates how to use the `orderby` clause in a LINQ query to sort the array of teachers by family name, in ascending order. + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="PrimaryAscendingSortQuery"::: + +The equivalent query written using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="PrimaryAscendingSortMethod"::: + +## Primary Descending Sort + +The next example demonstrates how to use the `orderby descending` clause in a LINQ query to sort the teachers by family name, in descending order. + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="PrimaryDescendingSortQuery"::: + +The equivalent query written using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="PrimaryDescendingSortMethod"::: + +## Secondary Ascending Sort + +The following example demonstrates how to use the `orderby` clause in a LINQ query to perform a primary and secondary sort. The teachers are sorted primarily by city and secondarily by their family name, both in ascending order. + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="SecondaryAscendingSortQuery"::: + +The equivalent query written using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="SecondaryAscendingSortMethod"::: + +## Secondary Descending Sort + +The next example demonstrates how to use the `orderby descending` clause in a LINQ query to perform a primary sort, in ascending order, and a secondary sort, in descending order. The teachers are sorted primarily by city and secondarily by their family name. + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="SecondaryDescendingSortQuery"::: + +The equivalent query written using method syntax is shown in the following code: + +:::code language="csharp" source="./snippets/standard-query-operators/SortExamples.cs" id="SecondaryDescendingSortMethod"::: + +## See also + +- +- [orderby clause](../../language-reference/keywords/orderby-clause.md) +- [How to sort or filter text data by any word or field (LINQ) (C#)](../../programming-guide/concepts/linq//how-to-sort-or-filter-text-data-by-any-word-or-field-linq.md) diff --git a/docs/csharp/misc/cs1935.md b/docs/csharp/misc/cs1935.md index ad1a648f4937d..475846950a1e2 100644 --- a/docs/csharp/misc/cs1935.md +++ b/docs/csharp/misc/cs1935.md @@ -44,4 +44,4 @@ class Test ## See also -- [Standard Query Operators Overview](../programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview](../linq/standard-query-operators/index.md) diff --git a/docs/csharp/misc/cs1940.md b/docs/csharp/misc/cs1940.md index 241ee5e86b289..04ce057111905 100644 --- a/docs/csharp/misc/cs1940.md +++ b/docs/csharp/misc/cs1940.md @@ -47,4 +47,4 @@ class Test ## See also -- [Standard Query Operators Overview](../programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview](../linq/standard-query-operators/index.md) diff --git a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md index 01de6be69367e..3e3e35d2afcee 100644 --- a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md +++ b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md @@ -135,7 +135,7 @@ For a class library that you implemented, you shouldn't use extension methods to - [C# Programming Guide](../index.md) - [Parallel Programming Samples (these include many example extension methods)](/samples/browse/?products=dotnet&term=parallel) - [Lambda Expressions](../../language-reference/operators/lambda-expressions.md) -- [Standard Query Operators Overview](../concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview](../../linq/standard-query-operators/index.md) - [Conversion rules for Instance parameters and their impact](/archive/blogs/sreekarc/conversion-rules-for-instance-parameters-and-their-impact) - [Extension methods Interoperability between languages](/archive/blogs/sreekarc/extension-methods-interoperability-between-languages) - [Extension methods and Curried Delegates](/archive/blogs/sreekarc/extension-methods-and-curried-delegates) diff --git a/docs/csharp/programming-guide/concepts/linq/aggregation-operations.md b/docs/csharp/programming-guide/concepts/linq/aggregation-operations.md deleted file mode 100644 index 7fecb9cf135fc..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/aggregation-operations.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "Aggregation operations (C#)" -description: Learn about methods for performing an aggregation operation. An aggregation operation computes a single value from a collection of values. -ms.date: 09/07/2021 -ms.assetid: 6fc035e5-7639-48b8-bc7f-b093dd31b039 ---- - -# Aggregation operations (C#) - -An aggregation operation computes a single value from a collection of values. An example of an aggregation operation is calculating the average daily temperature from a month's worth of daily temperature values. - -The following illustration shows the results of two different aggregation operations on a sequence of numbers. The first operation sums the numbers. The second operation returns the maximum value in the sequence. - -![Illustration that shows LINQ aggregation operations.](./media/aggregation-operations/linq-aggregation-operations.png) - -The standard query operator methods that perform aggregation operations are listed in the following section. - -## Methods - -| Method name | description | C# query expression syntax | More information | -|--|--|--|--| -| Aggregate | Performs a custom aggregation operation on the values of a collection. | Not applicable. |
| -| Average | Calculates the average value of a collection of values. | Not applicable. |
| -| Count | Counts the elements in a collection, optionally only those elements that satisfy a predicate function. | Not applicable. |
| -| LongCount | Counts the elements in a large collection, optionally only those elements that satisfy a predicate function. | Not applicable. |
| -| Max or MaxBy | Determines the maximum value in a collection. | Not applicable. |


| -| Min or MinBy | Determines the minimum value in a collection. | Not applicable. |


| -| Sum | Calculates the sum of the values in a collection. | Not applicable. |
| - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [How to compute column values in a CSV text file (LINQ) (C#)](./how-to-compute-column-values-in-a-csv-text-file-linq.md) -- [How to query for the largest file or files in a directory tree (LINQ) (C#)](./how-to-query-for-the-largest-file-or-files-in-a-directory-tree-linq.md) -- [How to query for the total number of bytes in a set of folders (LINQ) (C#)](./how-to-query-for-the-total-number-of-bytes-in-a-set-of-folders-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/concatenation-operations.md b/docs/csharp/programming-guide/concepts/linq/concatenation-operations.md deleted file mode 100644 index 97272618b6cbe..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/concatenation-operations.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: "Concatenation Operations (C#)" -description: Learn about concatenation and the standard query operator methods that perform concatenation in LINQ in C#. -ms.date: 07/20/2015 -ms.assetid: 890ee104-c590-457e-8a7f-b2b5da0fb417 ---- -# Concatenation Operations (C#) - -Concatenation refers to the operation of appending one sequence to another. - - The following illustration depicts a concatenation operation on two sequences of characters. - - ![Graphic showing concatenation of two sequences.](./media/concatenation-operations/concatenation-two-sequences.png) - - The standard query operator methods that perform concatenation are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|Concat|Concatenates two sequences to form one sequence.|Not applicable.|

| - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [How to combine and compare string collections (LINQ) (C#)](./how-to-combine-and-compare-string-collections-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/data-transformations-with-linq.md b/docs/csharp/programming-guide/concepts/linq/data-transformations-with-linq.md deleted file mode 100644 index eaba650f2ad09..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/data-transformations-with-linq.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: "Data Transformations with LINQ (C#)" -description: Learn how to use LINQ queries in C# to transform data. You can modify the sequence by sorting and grouping and create new types by using the select clause. -ms.date: 07/20/2015 -helpviewer_keywords: - - "LINQ [C#], data transformations" - - "source elements [LINQ in C#]" - - "joining multiple inputs [LINQ in C#]" - - "multiple outputs for one output sequence [LINQ in C#]" - - "subset of source elements [LINQ in C#]" - - "data sources [LINQ in C#], data transformations" - - "data transformations [LINQ in C#]" -ms.assetid: 674eae9e-bc72-4a88-aed3-802b45b25811 ---- -# Data Transformations with LINQ (C#) - -Language-Integrated Query (LINQ) is not only about retrieving data. It is also a powerful tool for transforming data. By using a LINQ query, you can use a source sequence as input and modify it in many ways to create a new output sequence. You can modify the sequence itself without modifying the elements themselves by sorting and grouping. But perhaps the most powerful feature of LINQ queries is the ability to create new types. This is accomplished in the [select](../../../language-reference/keywords/select-clause.md) clause. For example, you can perform the following tasks: - -- Merge multiple input sequences into a single output sequence that has a new type. - -- Create output sequences whose elements consist of only one or several properties of each element in the source sequence. - -- Create output sequences whose elements consist of the results of operations performed on the source data. - -- Create output sequences in a different format. For example, you can transform data from SQL rows or text files into XML. - - These are just several examples. Of course, these transformations can be combined in various ways in the same query. Furthermore, the output sequence of one query can be used as the input sequence for a new query. - -## Joining Multiple Inputs into One Output Sequence - - You can use a LINQ query to create an output sequence that contains elements from more than one input sequence. The following example shows how to combine two in-memory data structures, but the same principles can be applied to combine data from XML or SQL or DataSet sources. Assume the following two class types: - - [!code-csharp[CsLINQGettingStarted#7](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#7)] - - The following example shows the query: - - [!code-csharp[CSLinqGettingStarted#8](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#8)] - - For more information, see [join clause](../../../language-reference/keywords/join-clause.md) and [select clause](../../../language-reference/keywords/select-clause.md). - -## Selecting a Subset of each Source Element - - There are two primary ways to select a subset of each element in the source sequence: - -1. To select just one member of the source element, use the dot operation. In the following example, assume that a `Customer` object contains several public properties including a string named `City`. When executed, this query will produce an output sequence of strings. - - ```csharp - var query = from cust in Customers - select cust.City; - ``` - -2. To create elements that contain more than one property from the source element, you can use an object initializer with either a named object or an anonymous type. The following example shows the use of an anonymous type to encapsulate two properties from each `Customer` element: - - ```csharp - var query = from cust in Customer - select new {Name = cust.Name, City = cust.City}; - ``` - - For more information, see [Object and Collection Initializers](../../classes-and-structs/object-and-collection-initializers.md) and [Anonymous Types](../../../fundamentals/types/anonymous-types.md). - -## Transforming in-Memory Objects into XML - - LINQ queries make it easy to transform data between in-memory data structures, SQL databases, ADO.NET Datasets and XML streams or documents. The following example transforms objects in an in-memory data structure into XML elements. - - [!code-csharp[CsLINQGettingStarted#9](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#9)] - - The code produces the following XML output: - -```xml - - - Svetlana - Omelchenko - 97,92,81,60 - - - Claire - O'Donnell - 75,84,91,39 - - - Sven - Mortensen - 88,94,65,91 - - -``` - - For more information, see [Creating XML Trees in C# (LINQ to XML)](../../../../standard/linq/create-xml-trees.md). - -## Performing Operations on Source Elements - - An output sequence might not contain any elements or element properties from the source sequence. The output might instead be a sequence of values that is computed by using the source elements as input arguments. - - The following query will take a sequence of numbers that represent radii of circles, calculate the area for each radius, and return an output sequence containing strings formatted with the calculated area. - - Each string for the output sequence will be formatted using [string interpolation](../../../language-reference/tokens/interpolated.md). An interpolated string will have a `$` in front of the string's opening quotation mark, and operations can be performed within curly braces placed inside the interpolated string. Once those operations are performed, the results will be concatenated. - -> [!NOTE] -> Calling methods in query expressions is not supported if the query will be translated into some other domain. For example, you cannot call an ordinary C# method in [!INCLUDE[vbtecdlinq](~/includes/vbtecdlinq-md.md)] because SQL Server has no context for it. However, you can map stored procedures to methods and call those. For more information, see [Stored Procedures](../../../../framework/data/adonet/sql/linq/stored-procedures.md). - - [!code-csharp[CsLINQGettingStarted#10](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#10)] - -## See also - -- [Language-Integrated Query (LINQ) (C#)](/dotnet/csharp/linq/) -- [LINQ to SQL](../../../../framework/data/adonet/sql/linq/index.md) -- [LINQ to DataSet](../../../../framework/data/adonet/linq-to-dataset.md) -- [LINQ to XML (C#)](../../../../standard/linq/linq-xml-overview.md) -- [select clause](../../../language-reference/keywords/select-clause.md) diff --git a/docs/csharp/programming-guide/concepts/linq/element-operations.md b/docs/csharp/programming-guide/concepts/linq/element-operations.md deleted file mode 100644 index 4f1134088c4bb..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/element-operations.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Element operations (C#)" -description: Learn about the standard query operator methods that do element operations, which return a single element from a sequence in C#. -ms.date: 09/07/2021 -ms.assetid: 283206c9-3246-4c48-b01a-d9de409a7231 ---- - -# Element operations (C#) - -Element operations return a single, specific element from a sequence. - -The standard query operator methods that perform element operations are listed in the following section. - -## Methods - -| Method name | Description | C# query expression syntax | More information | -|--|--|--|--| -| ElementAt | Returns the element at a specified index in a collection. | Not applicable. |
| -| ElementAtOrDefault | Returns the element at a specified index in a collection or a default value if the index is out of range. | Not applicable. |
| -| First | Returns the first element of a collection, or the first element that satisfies a condition. | Not applicable. |
| -| FirstOrDefault | Returns the first element of a collection, or the first element that satisfies a condition. Returns a default value if no such element exists. | Not applicable. |

| -| Last | Returns the last element of a collection, or the last element that satisfies a condition. | Not applicable. |
| -| LastOrDefault | Returns the last element of a collection, or the last element that satisfies a condition. Returns a default value if no such element exists. | Not applicable. |
| -| Single | Returns the only element of a collection or the only element that satisfies a condition. Throws an if there is no element or more than one element to return. | Not applicable. |
| -| SingleOrDefault | Returns the only element of a collection or the only element that satisfies a condition. Returns a default value if there is no element to return. Throws an if there is more than one element to return. | Not applicable. |
| - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [How to query for the largest file or files in a directory tree (LINQ) (C#)](./how-to-query-for-the-largest-file-or-files-in-a-directory-tree-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/equality-operations.md b/docs/csharp/programming-guide/concepts/linq/equality-operations.md deleted file mode 100644 index a7b037d2bf9f9..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/equality-operations.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: "Equality Operations (C#)" -description: Learn about the equality operators in LINQ in C#, where two sequences whose elements are equal and which have the same number of elements are equal. -ms.date: 07/20/2015 -ms.assetid: 9d9a5bf2-f211-4865-bd19-c59ffa004615 ---- -# Equality Operations (C#) - -Two sequences whose corresponding elements are equal and which have the same number of elements are considered equal. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|SequenceEqual|Determines whether two sequences are equal by comparing elements in a pair-wise manner.|Not applicable.|

| - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [How to compare the contents of two folders (LINQ) (C#)](./how-to-compare-the-contents-of-two-folders-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/filtering-data.md b/docs/csharp/programming-guide/concepts/linq/filtering-data.md deleted file mode 100644 index c7cb36ebedbb4..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/filtering-data.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: "Filtering Data (C#)" -description: Filtering, also known as selection, restricts results based on a condition. Learn about the standard query operator methods in LINQ in C# that perform filtering. -ms.date: 07/20/2015 -ms.assetid: fbaece0d-0f23-47f7-89c5-f3ea8db692b6 ---- -# Filtering Data (C#) - -Filtering refers to the operation of restricting the result set to contain only those elements that satisfy a specified condition. It is also known as selection. - - The following illustration shows the results of filtering a sequence of characters. The predicate for the filtering operation specifies that the character must be 'A'. - - ![Diagram that shows a LINQ filtering operation](./media/filtering-data/linq-filter-operation.png) - - The standard query operator methods that perform selection are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|OfType|Selects values, depending on their ability to be cast to a specified type.|Not applicable.|

| -|Where|Selects values that are based on a predicate function.|`where`|

| - -## Query Expression Syntax Example - - The following example uses the `where` clause to filter from an array those strings that have a specific length. - -```csharp -string[] words = [ "the", "quick", "brown", "fox", "jumps" ]; - -IEnumerable query = from word in words - where word.Length == 3 - select word; - -foreach (string str in query) - Console.WriteLine(str); - -/* This code produces the following output: - - the - fox -*/ -``` - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [where clause](../../../language-reference/keywords/where-clause.md) -- [Dynamically specify predicate filters at run time](../../../linq/dynamically-specify-predicate-filters-at-runtime.md) -- [How to query an assembly's metadata with Reflection (LINQ) (C#)](../../../advanced-topics/reflection-and-attributes/how-to-query-assembly-metadata-with-reflection-linq.md) -- [How to query for files with a specified attribute or name (C#)](./how-to-query-for-files-with-a-specified-attribute-or-name.md) -- [How to sort or filter text data by any word or field (LINQ) (C#)](./how-to-sort-or-filter-text-data-by-any-word-or-field-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/generation-operations.md b/docs/csharp/programming-guide/concepts/linq/generation-operations.md deleted file mode 100644 index 4696483789d67..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/generation-operations.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "Generation Operations (C#)" -description: Generation creates a new sequence of values. Learn about the standard query operator methods in LINQ in C# that perform generation. -ms.date: 07/20/2015 -ms.assetid: 1c421b3a-5954-448c-bcac-a27798d1858f ---- -# Generation Operations (C#) - -Generation refers to creating a new sequence of values. - - The standard query operator methods that perform generation are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|DefaultIfEmpty|Replaces an empty collection with a default valued singleton collection.|Not applicable.|

| -|Empty|Returns an empty collection.|Not applicable.|| -|Range|Generates a collection that contains a sequence of numbers.|Not applicable.|| -|Repeat|Generates a collection that contains one repeated value.|Not applicable.|| - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) diff --git a/docs/csharp/programming-guide/concepts/linq/grouping-data.md b/docs/csharp/programming-guide/concepts/linq/grouping-data.md deleted file mode 100644 index 43a329c6b5d1d..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/grouping-data.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: "Grouping Data (C#)" -description: Grouping puts data into groups of elements that share an attribute. Learn about the standard query operator methods in LINQ in C# that group data elements. -ms.date: 07/20/2015 -ms.assetid: e414e9e4-343a-4e6e-858f-4a30c5e64492 ---- -# Grouping Data (C#) - -Grouping refers to the operation of putting data into groups so that the elements in each group share a common attribute. - - The following illustration shows the results of grouping a sequence of characters. The key for each group is the character. - - ![Diagram that shows a LINQ Grouping operation.](./media/grouping-data/linq-group-operation.png) - - The standard query operator methods that group data elements are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|GroupBy|Groups elements that share a common attribute. Each group is represented by an object.|`group … by`

-or-

`group … by … into …`|

| -|ToLookup|Inserts elements into a (a one-to-many dictionary) based on a key selector function.|Not applicable.|| - -## Query Expression Syntax Example - - The following code example uses the `group by` clause to group integers in a list according to whether they are even or odd. - -```csharp -List numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208]; - -IEnumerable> query = from number in numbers - group number by number % 2; - -foreach (var group in query) -{ - Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:"); - foreach (int i in group) - Console.WriteLine(i); -} - -/* This code produces the following output: - - Odd numbers: - 35 - 3987 - 199 - 329 - - Even numbers: - 44 - 200 - 84 - 4 - 446 - 208 -*/ -``` - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [group clause](../../../language-reference/keywords/group-clause.md) -- [Create a nested group](../../../linq/create-a-nested-group.md) -- [How to group files by extension (LINQ) (C#)](./how-to-group-files-by-extension-linq.md) -- [Group query results](../../../linq/group-query-results.md) -- [Perform a subquery on a grouping operation](../../../linq/perform-a-subquery-on-a-grouping-operation.md) -- [How to split a file into many files by using groups (LINQ) (C#)](./how-to-split-a-file-into-many-files-by-using-groups-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/join-operations.md b/docs/csharp/programming-guide/concepts/linq/join-operations.md deleted file mode 100644 index 2e997bc51012f..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/join-operations.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: "Join Operations (C#)" -description: A join of two data sources associates objects with objects that share an attribute across data sources. Learn about join methods in the LINQ framework in C#. -ms.date: 07/20/2015 -ms.assetid: 5105e0da-1267-4c00-837a-f0e9602279b8 -no-loc: [Join, GroupJoin] ---- -# Join Operations (C#) - -A *join* of two data sources is the association of objects in one data source with objects that share a common attribute in another data source. - - Joining is an important operation in queries that target data sources whose relationships to each other cannot be followed directly. In object-oriented programming, this could mean a correlation between objects that is not modeled, such as the backwards direction of a one-way relationship. An example of a one-way relationship is a Customer class that has a property of type City, but the City class does not have a property that is a collection of Customer objects. If you have a list of City objects and you want to find all the customers in each city, you could use a join operation to find them. - - The join methods provided in the LINQ framework are and . These methods perform equijoins, or joins that match two data sources based on equality of their keys. (For comparison, Transact-SQL supports join operators other than 'equals', for example the 'less than' operator.) In relational database terms, implements an inner join, a type of join in which only those objects that have a match in the other data set are returned. The method has no direct equivalent in relational database terms, but it implements a superset of inner joins and left outer joins. A left outer join is a join that returns each element of the first (left) data source, even if it has no correlated elements in the other data source. - - The following illustration shows a conceptual view of two sets and the elements within those sets that are included in either an inner join or a left outer join. - - ![Two overlapping circles showing inner/outer.](./media/join-operations/join-method-overlapping-circles.png) - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|Join|Joins two sequences based on key selector functions and extracts pairs of values.|`join … in … on … equals …`|

| -|GroupJoin|Joins two sequences based on key selector functions and groups the resulting matches for each element.|`join … in … on … equals … into …`|

| - -## Query expression syntax examples - -### Join - -The following example uses the `join … in … on … equals …` clause to join two sequences based on specific value: - -[!code-csharp[Join](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/JoinOperation.cs#Join)] - -### GroupJoin - -The following example uses the `join … in … on … equals … into …` clause to join two sequences based on specific value and groups the resulting matches for each element: - -[!code-csharp[GroupJoin](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/JoinOperation.cs#GroupJoin)] - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [Anonymous Types](../../../fundamentals/types/anonymous-types.md) -- [Formulate Joins and Cross-Product Queries](../../../../framework/data/adonet/sql/linq/formulate-joins-and-cross-product-queries.md) -- [join clause](../../../language-reference/keywords/join-clause.md) -- [Join by using composite keys](../../../linq/join-by-using-composite-keys.md) -- [How to join content from dissimilar files (LINQ) (C#)](./how-to-join-content-from-dissimilar-files-linq.md) -- [Order the results of a join clause](../../../linq/order-the-results-of-a-join-clause.md) -- [Perform custom join operations](../../../linq/perform-custom-join-operations.md) -- [Perform grouped joins](../../../linq/perform-grouped-joins.md) -- [Perform inner joins](../../../linq/perform-inner-joins.md) -- [Perform left outer joins](../../../linq/perform-left-outer-joins.md) -- [How to populate object collections from multiple sources (LINQ) (C#)](./how-to-populate-object-collections-from-multiple-sources-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/media/aggregation-operations/linq-aggregation-operations.png b/docs/csharp/programming-guide/concepts/linq/media/aggregation-operations/linq-aggregation-operations.png deleted file mode 100644 index d4161326d2c72..0000000000000 Binary files a/docs/csharp/programming-guide/concepts/linq/media/aggregation-operations/linq-aggregation-operations.png and /dev/null differ diff --git a/docs/csharp/programming-guide/concepts/linq/media/concatenation-operations/concatenation-two-sequences.png b/docs/csharp/programming-guide/concepts/linq/media/concatenation-operations/concatenation-two-sequences.png deleted file mode 100644 index a264885e7d4e0..0000000000000 Binary files a/docs/csharp/programming-guide/concepts/linq/media/concatenation-operations/concatenation-two-sequences.png and /dev/null differ diff --git a/docs/csharp/programming-guide/concepts/linq/projection-operations.md b/docs/csharp/programming-guide/concepts/linq/projection-operations.md deleted file mode 100644 index a2cb4d0e1286e..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/projection-operations.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -title: "Projection operations (C#)" -description: Learn about projection operations. These operations transform an object into a new form that often consists only of properties that will be used later. -ms.date: 09/10/2021 -ms.assetid: 98df573a-aad9-4b8c-9a71-844be2c4fb41 ---- - -# Projection operations (C#) - -Projection refers to the operation of transforming an object into a new form that often consists only of those properties that will be subsequently used. By using projection, you can construct a new type that is built from each object. You can project a property and perform a mathematical function on it. You can also project the original object without changing it. - -The standard query operator methods that perform projection are listed in the following section. - -## Methods - -| Method names | Description | C# query expression syntax | More information | -|--|--|--|--| -| Select | Projects values that are based on a transform function. | `select` |
| -| SelectMany | Projects sequences of values that are based on a transform function and then flattens them into one sequence. | Use multiple `from` clauses |
| -| Zip | Produces a sequence of tuples with elements from 2-3 specified sequences. | Not applicable. |
| - -## `Select` - -The following example uses the `select` clause to project the first letter from each string in a list of strings. - -```csharp -List words = ["an", "apple", "a", "day" ]; - -var query = from word in words - select word.Substring(0, 1); - -foreach (string s in query) - Console.WriteLine(s); - -/* This code produces the following output: - - a - a - a - d -*/ -``` - -## `SelectMany` - -The following example uses multiple `from` clauses to project each word from each string in a list of strings. - -```csharp -List phrases = ["an apple a day", "the quick brown fox"]; - -var query = from phrase in phrases - from word in phrase.Split(' ') - select word; - -foreach (string s in query) - Console.WriteLine(s); - -/* This code produces the following output: - - an - apple - a - day - the - quick - brown - fox -*/ -``` - -## `Zip` - -There are several overloads for the `Zip` projection operator. All of the `Zip` methods work on sequences of two or more possibly heterogenous types. The first two overloads return tuples, with the corresponding positional type from the given sequences. - -Consider the following collections: - -:::code source="snippets/projection/Program.cs" id="NumbersAndLetters"::: - -To project these sequences together, use the operator: - -:::code source="snippets/projection/Program.ZipTuple.cs" id="ZipTuple"::: - -> [!IMPORTANT] -> The resulting sequence from a zip operation is never longer in length than the shortest sequence. The `numbers` and `letters` collections differ in length, and the resulting sequence omits the last element from the `numbers` collection, as it has nothing to zip with. - -The second overload accepts a `third` sequence. Let's create another collection, namely `emoji`: - -:::code source="snippets/projection/Program.cs" id="Emoji"::: - -To project these sequences together, use the operator: - -:::code source="snippets/projection/Program.ZipTriple.cs" id="ZipTriple"::: - -Much like the previous overload, the `Zip` method projects a tuple, but this time with three elements. - -The third overload accepts a `Func` argument that acts as a results selector. Given the two types from the sequences being zipped, you can project a new resulting sequence. - -:::code source="snippets/projection/Program.ZipResult.cs" id="ZipResultSelector"::: - -With the preceding `Zip` overload, the specified function is applied to the corresponding elements `numbers` and `letter`, producing a sequence of the `string` results. - -## `Select` versus `SelectMany` - -The work of both `Select` and `SelectMany` is to produce a result value (or values) from source values. `Select` produces one result value for every source value. The overall result is therefore a collection that has the same number of elements as the source collection. In contrast, `SelectMany` produces a single overall result that contains concatenated sub-collections from each source value. The transform function that is passed as an argument to `SelectMany` must return an enumerable sequence of values for each source value. These enumerable sequences are then concatenated by `SelectMany` to create one large sequence. - -The following two illustrations show the conceptual difference between the actions of these two methods. In each case, assume that the selector (transform) function selects the array of flowers from each source value. - -This illustration depicts how `Select` returns a collection that has the same number of elements as the source collection. - -![Graphic that shows the action of Select()](./media/projection-operations/select-action-graphic.png) - -This illustration depicts how `SelectMany` concatenates the intermediate sequence of arrays into one final result value that contains each value from each intermediate array. - -![Graphic showing the action of SelectMany().](./media/projection-operations/select-many-action-graphic.png) - -### Code example - -The following example compares the behavior of `Select` and `SelectMany`. The code creates a "bouquet" of flowers by taking the items from each list of flower names in the source collection. In this example, the "single value" that the transform function uses is itself a collection of values. This requires the extra `foreach` loop in order to enumerate each string in each sub-sequence. - -```csharp -class Bouquet -{ - public List Flowers { get; set; } -} - -static void SelectVsSelectMany() -{ - List bouquets = - [ - new Bouquet { Flowers = new List { "sunflower", "daisy", "daffodil", "larkspur" }}, - new Bouquet { Flowers = new List { "tulip", "rose", "orchid" }}, - new Bouquet { Flowers = new List { "gladiolis", "lily", "snapdragon", "aster", "protea" }}, - new Bouquet { Flowers = new List { "larkspur", "lilac", "iris", "dahlia" }} - ]; - - IEnumerable> query1 = bouquets.Select(bq => bq.Flowers); - - IEnumerable query2 = bouquets.SelectMany(bq => bq.Flowers); - - Console.WriteLine("Results by using Select():"); - // Note the extra foreach loop here. - foreach (IEnumerable collection in query1) - foreach (string item in collection) - Console.WriteLine(item); - - Console.WriteLine("\nResults by using SelectMany():"); - foreach (string item in query2) - Console.WriteLine(item); - - /* This code produces the following output: - - Results by using Select(): - sunflower - daisy - daffodil - larkspur - tulip - rose - orchid - gladiolis - lily - snapdragon - aster - protea - larkspur - lilac - iris - dahlia - - Results by using SelectMany(): - sunflower - daisy - daffodil - larkspur - tulip - rose - orchid - gladiolis - lily - snapdragon - aster - protea - larkspur - lilac - iris - dahlia - */ -} -``` - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [select clause](../../../language-reference/keywords/select-clause.md) -- [How to populate object collections from multiple sources (LINQ) (C#)](./how-to-populate-object-collections-from-multiple-sources-linq.md) -- [How to split a file into many files by using groups (LINQ) (C#)](./how-to-split-a-file-into-many-files-by-using-groups-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/quantifier-operations.md b/docs/csharp/programming-guide/concepts/linq/quantifier-operations.md deleted file mode 100644 index ca9c6e9611cff..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/quantifier-operations.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "Quantifier Operations (C#)" -titleSuffix: LINQ -description: Learn about quantifier operations in LINQ. These methods, 'All', 'Any', and 'Contains', return a Boolean value indicating whether some or all elements in a sequence satisfy a condition. -ms.date: 03/30/2022 ---- -# Quantifier operations in LINQ (C#) - -Quantifier operations return a value that indicates whether some or all of the elements in a sequence satisfy a condition. - - The following illustration depicts two different quantifier operations on two different source sequences. The first operation asks if any of the elements are the character 'A'. The second operation asks if all the elements are the character 'A'. Both methods return `true` in this example. - - ![LINQ Quantifier Operations](./media/quantifier-operations/linq-quantifier-operations.png) - - The standard query operator methods that perform quantifier operations are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|All|Determines whether all the elements in a sequence satisfy a condition.|Not applicable.|
| -|Any|Determines whether any elements in a sequence satisfy a condition.|Not applicable.|
| -|Contains|Determines whether a sequence contains a specified element.|Not applicable.|
| - -## Query Expression Syntax Examples - -### All - -The following example uses the `All` to check that all strings are of a specific length. - -[!code-csharp[All](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs#All)] - -### Any - -The following example uses the `Any` to check that any strings are start with 'o'. - -[!code-csharp[Any](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs#Any)] - -### Contains - -The following example uses the `Contains` to check an array have a specific element. - -[!code-csharp[Contains](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs#Contains)] - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [Dynamically specify predicate filters at run time](../../../linq/dynamically-specify-predicate-filters-at-runtime.md) -- [How to query for sentences that contain a specified set of words (LINQ) (C#)](./how-to-query-for-sentences-that-contain-a-specified-set-of-words-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/set-operations.md b/docs/csharp/programming-guide/concepts/linq/set-operations.md deleted file mode 100644 index 3a0994f9c4c43..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/set-operations.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: "Set operations (C#)" -description: Learn about set operations and the standard query operator methods that perform set operations in LINQ in C#. -ms.date: 01/17/2024 -ms.assetid: 7c589367-ef8f-4161-9050-642c47e6bf63 ---- - -# Set operations (C#) - -Set operations in LINQ refer to query operations that produce a result set that's based on the presence or absence of equivalent elements within the same or separate collections. - -The standard query operator methods that perform set operations are listed in the following section. - -## Methods - -| Method names | Description | C# query expression syntax | More information | -|--|--|--|--| -| `Distinct` or `DistinctBy` | Removes duplicate values from a collection. | Not applicable. |


| -| `Except` or `ExceptBy` | Returns the set difference, which means the elements of one collection that don't appear in a second collection. | Not applicable. |


| -| `Intersect` or `IntersectBy` | Returns the set intersection, which means elements that appear in each of two collections. | Not applicable. |


| -| `Union` or `UnionBy` | Returns the set union, which means unique elements that appear in either of two collections. | Not applicable. |


| - -## Examples - -Some of the following examples rely on a `record` type that represents the planets in our solar system. - -:::code source="snippets/set-operators/Planet.cs"::: - -The `Planet` is a positional record, which requires a `Name`, `Type`, and `OrderFromSun` arguments to instantiate it. There are several `static readonly` planet instances on the `Planet` type. These are convenience-based definitions for well-known planets. The `Type` member identifies the planet type. - -:::code source="snippets/set-operators/PlanetType.cs"::: - -## `Distinct` and `DistinctBy` - -The following example depicts the behavior of the method on a sequence of strings. The returned sequence contains the unique elements from the input sequence. - -![Graphic showing the behavior of Distinct().](./media/set-operations/distinct-method-behavior.png) - -[!code-csharp-interactive[Distinct](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs#1)] - -The [`DistinctBy`](xref:System.Linq.Enumerable.DistinctBy%2A?displayProperty=nameWithType) is an alternative approach to `Distinct` that takes a `keySelector`. The `keySelector` is used as the comparative discriminator of the source type. Consider the following planet array: - -:::code source="snippets/set-operators/Program.DistinctBy.cs" id="Planets"::: - -In the following code, planets are discriminated based on their `PlanetType`, and the first planet of each type is displayed: - -:::code source="snippets/set-operators/Program.DistinctBy.cs" id="DistinctBy"::: - -In the preceding C# code: - -- The `Planet` array is filtered distinctly to the first occurrence of each unique planet type. -- The resulting `planet` instances are written to the console. - -## `Except` and `ExceptBy` - -The following example depicts the behavior of . The returned sequence contains only the elements from the first input sequence that aren't in the second input sequence. - -![Graphic showing the action of Except().](./media/set-operations/except-behavior-graphic.png "Shows the behavior of Except.") - -[!code-csharp-interactive[Except](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs#2)] - -The method is an alternative approach to `Except` that takes two sequences of possibly heterogenous types and a `keySelector`. The `keySelector` is the same type as the first collection's type, and it's used as the comparative discriminator between the two collections. Consider the following planet array and plants to exclude, represented as their order from the sun: - -:::code source="snippets/set-operators/Program.ExceptBy.cs" id="Planets"::: - -To find planets in the first collection that aren't in the second collection, you can project the planet's order from the sun value onto the `second` collection: - -:::code source="snippets/set-operators/Program.ExceptBy.cs" id="ExceptBy"::: - -In the preceding C# code: - -- The `planets` array is filtered to only those planets that aren't in the `planetsToExclude` array. -- The `planetsToExclude` array is defined by their respective `OrderFromSun` value. -- The call to `ExceptBy` results in a new set of values that are written to the console. - -The new set of values is of type `Planet`, which is the type of the first collection. Each `planet` in the `planets` array that doesn't have a corresponding order from the sun value in the `planetsToExclude` array is written to the console. - -## `Intersect` and `IntersectBy` - -The following example depicts the behavior of . The returned sequence contains the elements that are common to both of the input sequences. - -![Graphic showing the intersection of two sequences.](./media/set-operations/intersection-two-sequences.png) - -[!code-csharp-interactive[Intersect](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs#3)] - -The method is an alternative approach to `Intersect` that takes two sequences of possibly heterogenous types and a `keySelector`. The `keySelector` is used as the comparative discriminator of the second collection's type. Consider the following planet arrays: - -:::code source="snippets/set-operators/Program.IntersectBy.cs" id="Planets"::: - -There are two arrays of planets; one represents the first five planets from the sun and the second represents the last five planets from the sun. Since the `Planet` type is a positional `record` type, you can use its value comparison semantics in the form of the `keySelector`: - -:::code source="snippets/set-operators/Program.IntersectBy.cs" id="IntersectBy"::: - -In the preceding C# code: - -- The two `Planet` arrays are intersected by their value comparison semantics. -- Only planets that are found in both arrays are present in the resulting sequence. -- The resulting `planet` instances are written to the console. - -## `Union` and `UnionBy` - -The following example depicts a union operation on two sequences of strings. The returned sequence contains the unique elements from both input sequences. - -![Graphic showing the union of two sequences.](./media/set-operations/union-operation-two-sequences.png) - -[!code-csharp-interactive[Union](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs#4)] - -The method is an alternative approach to `Union` that takes two sequences of the same type and a `keySelector`. The `keySelector` is used as the comparative discriminator of the source type. Consider the following planet arrays: - -:::code source="snippets/set-operators/Program.UnionBy.cs" id="Planets"::: - -To union these two collections into a single sequence, you provide the `keySelector`: - -:::code source="snippets/set-operators/Program.UnionBy.cs" id="UnionBy"::: - -In the preceding C# code: - -- The two `Planet` arrays are woven together using their `record` value comparison semantics. -- The resulting `planet` instances are written to the console. - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [How to combine and compare string collections (LINQ) (C#)](./how-to-combine-and-compare-string-collections-linq.md) -- [How to find the set difference between two lists (LINQ) (C#)](./how-to-find-the-set-difference-between-two-lists-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/extensions/LinqExtensions.cs b/docs/csharp/programming-guide/concepts/linq/snippets/extensions/LinqExtensions.cs index 7df6d784e1f86..7c3c837fb3841 100644 --- a/docs/csharp/programming-guide/concepts/linq/snippets/extensions/LinqExtensions.cs +++ b/docs/csharp/programming-guide/concepts/linq/snippets/extensions/LinqExtensions.cs @@ -1,69 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace Custom.Linq.Extensions; -namespace Custom.Linq.Extensions +// +public static class EnumerableExtension { - // - public static class EnumerableExtension + public static double Median(this IEnumerable? source) { - public static double Median(this IEnumerable? source) + if (source is null || !source.Any()) { - if (source is null || !source.Any()) - { - throw new InvalidOperationException("Cannot compute median for a null or empty set."); - } + throw new InvalidOperationException("Cannot compute median for a null or empty set."); + } - var sortedList = - source.OrderBy(number => number).ToList(); + var sortedList = + source.OrderBy(number => number).ToList(); - int itemIndex = sortedList.Count / 2; + int itemIndex = sortedList.Count / 2; - if (sortedList.Count % 2 == 0) - { - // Even number of items. - return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2; - } - else - { - // Odd number of items. - return sortedList[itemIndex]; - } + if (sortedList.Count % 2 == 0) + { + // Even number of items. + return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2; + } + else + { + // Odd number of items. + return sortedList[itemIndex]; } } - // +} +// - public static class OtherExtensions - { - // - // int overload - public static double Median(this IEnumerable source) => - (from number in source select (double)number).Median(); - // +public static class OtherExtensions +{ + // + // int overload + public static double Median(this IEnumerable source) => + (from number in source select (double)number).Median(); + // - // - // generic overload - public static double Median( - this IEnumerable numbers, Func selector) => - (from num in numbers select selector(num)).Median(); - // + // + // generic overload + public static double Median( + this IEnumerable numbers, Func selector) => + (from num in numbers select selector(num)).Median(); + // - // - // Extension method for the IEnumerable interface. - // The method returns every other element of a sequence. - public static IEnumerable AlternateElements(this IEnumerable source) + // + // Extension method for the IEnumerable interface. + // The method returns every other element of a sequence. + public static IEnumerable AlternateElements(this IEnumerable source) + { + int index = 0; + foreach (T element in source) { - int index = 0; - foreach (T element in source) + if (index % 2 == 0) { - if (index % 2 == 0) - { - yield return element; - } - - index++; + yield return element; } + + index++; } - // } + // } diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/linq-snippets.sln b/docs/csharp/programming-guide/concepts/linq/snippets/linq-snippets.sln index fbfdafab72dae..74e1a0f816460 100644 --- a/docs/csharp/programming-guide/concepts/linq/snippets/linq-snippets.sln +++ b/docs/csharp/programming-guide/concepts/linq/snippets/linq-snippets.sln @@ -3,36 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31612.314 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "set-operators", "set-operators\set-operators.csproj", "{9459A490-26DC-4F06-8AC8-BDBD6D09CA79}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linq", "extensions\linq.csproj", "{5097A709-D209-45BF-8A1F-96667DE90C37}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "projection", "projection\projection.csproj", "{F3A812B5-F587-48B5-8B32-27F066721672}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "partition", "partition\partition.csproj", "{E337A455-676D-4F6D-A40B-0CD9A2117BA9}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9459A490-26DC-4F06-8AC8-BDBD6D09CA79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9459A490-26DC-4F06-8AC8-BDBD6D09CA79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9459A490-26DC-4F06-8AC8-BDBD6D09CA79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9459A490-26DC-4F06-8AC8-BDBD6D09CA79}.Release|Any CPU.Build.0 = Release|Any CPU {5097A709-D209-45BF-8A1F-96667DE90C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5097A709-D209-45BF-8A1F-96667DE90C37}.Debug|Any CPU.Build.0 = Debug|Any CPU {5097A709-D209-45BF-8A1F-96667DE90C37}.Release|Any CPU.ActiveCfg = Release|Any CPU {5097A709-D209-45BF-8A1F-96667DE90C37}.Release|Any CPU.Build.0 = Release|Any CPU - {F3A812B5-F587-48B5-8B32-27F066721672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F3A812B5-F587-48B5-8B32-27F066721672}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F3A812B5-F587-48B5-8B32-27F066721672}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F3A812B5-F587-48B5-8B32-27F066721672}.Release|Any CPU.Build.0 = Release|Any CPU - {E337A455-676D-4F6D-A40B-0CD9A2117BA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E337A455-676D-4F6D-A40B-0CD9A2117BA9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E337A455-676D-4F6D-A40B-0CD9A2117BA9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E337A455-676D-4F6D-A40B-0CD9A2117BA9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/partition/Program.cs b/docs/csharp/programming-guide/concepts/linq/snippets/partition/Program.cs deleted file mode 100644 index 58469a06394dd..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/partition/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -int chunkNumber = 1; -foreach (int[] chunk in Enumerable.Range(0, 8).Chunk(3)) -{ - Console.WriteLine($"Chunk {chunkNumber++}:"); - foreach (int item in chunk) - { - Console.WriteLine($" {item}"); - } - - Console.WriteLine(); -} -// This code produces the following output: -// Chunk 1: -// 0 -// 1 -// 2 -// -//Chunk 2: -// 3 -// 4 -// 5 -// -//Chunk 3: -// 6 -// 7 diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/partition/partition.csproj b/docs/csharp/programming-guide/concepts/linq/snippets/partition/partition.csproj deleted file mode 100644 index ddb1b95b31479..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/partition/partition.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - true - - - diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipResult.cs b/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipResult.cs deleted file mode 100644 index c17ccf5c69585..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -public static partial class Program -{ - internal static void ZipResultExample( - IEnumerable numbers, - IEnumerable letters) - { - Console.WriteLine("Zip (TFirst, TSecond, Func):"); - - // - foreach (string result in - numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})")) - { - Console.WriteLine(result); - } - // This code produces the following output: - // 1 = A (65) - // 2 = B (66) - // 3 = C (67) - // 4 = D (68) - // 5 = E (69) - // 6 = F (70) - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTriple.cs b/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTriple.cs deleted file mode 100644 index 8ded4f23437b1..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTriple.cs +++ /dev/null @@ -1,27 +0,0 @@ -public static partial class Program -{ - internal static void ZipTripleExample( - IEnumerable numbers, - IEnumerable letters, - IEnumerable emoji) - { - Console.WriteLine("Zip (TFirst, TSecond, TThird):"); - - // - foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji)) - { - Console.WriteLine( - $"Number: {number} is zipped with letter: '{letter}' and emoji: {em}"); - } - // This code produces the following output: - // Number: 1 is zipped with letter: 'A' and emoji: 🤓 - // Number: 2 is zipped with letter: 'B' and emoji: 🔥 - // Number: 3 is zipped with letter: 'C' and emoji: 🎉 - // Number: 4 is zipped with letter: 'D' and emoji: 👀 - // Number: 5 is zipped with letter: 'E' and emoji: ⭐ - // Number: 6 is zipped with letter: 'F' and emoji: 💜 - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTuple.cs b/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTuple.cs deleted file mode 100644 index 94824ec188aad..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.ZipTuple.cs +++ /dev/null @@ -1,25 +0,0 @@ -public static partial class Program -{ - internal static void ZipTupleExample( - IEnumerable numbers, - IEnumerable letters) - { - Console.WriteLine("Zip (TFirst, TSecond):"); - - // - foreach ((int number, char letter) in numbers.Zip(letters)) - { - Console.WriteLine($"Number: {number} zipped with letter: '{letter}'"); - } - // This code produces the following output: - // Number: 1 zipped with letter: 'A' - // Number: 2 zipped with letter: 'B' - // Number: 3 zipped with letter: 'C' - // Number: 4 zipped with letter: 'D' - // Number: 5 zipped with letter: 'E' - // Number: 6 zipped with letter: 'F' - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.cs b/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.cs deleted file mode 100644 index abb67a2554af8..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/projection/Program.cs +++ /dev/null @@ -1,27 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -Console.OutputEncoding = System.Text.Encoding.UTF8; - -// -// An int array with 7 elements. -IEnumerable numbers = -[ - 1, 2, 3, 4, 5, 6, 7 -]; -// A char array with 6 elements. -IEnumerable letters = -[ - 'A', 'B', 'C', 'D', 'E', 'F' -]; -// -// -// A string array with 8 elements. -IEnumerable emoji = -[ - "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯" -]; -// - -Program.ZipTupleExample(numbers, letters); -Program.ZipTripleExample(numbers, letters, emoji); -Program.ZipResultExample(numbers, letters); diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/projection/projection.csproj b/docs/csharp/programming-guide/concepts/linq/snippets/projection/projection.csproj deleted file mode 100644 index ddb1b95b31479..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/projection/projection.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - true - - - diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Planet.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Planet.cs deleted file mode 100644 index fd97642051ca3..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Planet.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SolarSystem; - -public record class Planet( - string Name, - PlanetType Type, - int OrderFromSun) -{ - public static readonly Planet Mercury = - new(nameof(Mercury), PlanetType.Rock, 1); - - public static readonly Planet Venus = - new(nameof(Venus), PlanetType.Rock, 2); - - public static readonly Planet Earth = - new(nameof(Earth), PlanetType.Rock, 3); - - public static readonly Planet Mars = - new(nameof(Mars), PlanetType.Rock, 4); - - public static readonly Planet Jupiter = - new(nameof(Jupiter), PlanetType.Gas, 5); - - public static readonly Planet Saturn = - new(nameof(Saturn), PlanetType.Gas, 6); - - public static readonly Planet Uranus = - new(nameof(Uranus), PlanetType.Liquid, 7); - - public static readonly Planet Neptune = - new(nameof(Neptune), PlanetType.Liquid, 8); - - // Yes, I know... not technically a planet anymore - public static readonly Planet Pluto = - new(nameof(Pluto), PlanetType.Ice, 9); -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/PlanetType.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/PlanetType.cs deleted file mode 100644 index c9943471b10e5..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/PlanetType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SolarSystem; - -public enum PlanetType -{ - Rock, - Ice, - Gas, - Liquid -}; diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.DistinctBy.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.DistinctBy.cs deleted file mode 100644 index ebfdd2aabeda7..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.DistinctBy.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace SolarSystem; - -public static partial class Program -{ - internal static void DistinctByExample() - { - Console.WriteLine("DistinctBy:"); - - // - Planet[] planets = - [ - Planet.Mercury, - Planet.Venus, - Planet.Earth, - Planet.Mars, - Planet.Jupiter, - Planet.Saturn, - Planet.Uranus, - Planet.Neptune, - Planet.Pluto - ]; - // - - // - foreach (Planet planet in planets.DistinctBy(p => p.Type)) - { - Console.WriteLine(planet); - } - - // This code produces the following output: - // Planet { Name = Mercury, Type = Rock, OrderFromSun = 1 } - // Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 } - // Planet { Name = Uranus, Type = Liquid, OrderFromSun = 7 } - // Planet { Name = Pluto, Type = Ice, OrderFromSun = 9 } - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.ExceptBy.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.ExceptBy.cs deleted file mode 100644 index 5b3955c8842e9..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.ExceptBy.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace SolarSystem; - -public static partial class Program -{ - internal static void ExceptByExample() - { - Console.WriteLine("ExceptBy:"); - - // - Planet[] planets = - [ - Planet.Mercury, - Planet.Venus, - Planet.Earth, - Planet.Jupiter - ]; - - int[] planetsToExclude = - [ - 1, // Mercury - 2, // Venus - 5, // Jupiter - ]; - // - - // - foreach (Planet planet in - planets.ExceptBy( - planetsToExclude, static planet => planet.OrderFromSun)) - { - Console.WriteLine(planet); - } - - // This code produces the following output: - // Planet { Name = Venus, Type = Rock, OrderFromSun = 2 } - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.IntersectBy.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.IntersectBy.cs deleted file mode 100644 index 777a65e2d6912..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.IntersectBy.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace SolarSystem; - -public static partial class Program -{ - internal static void IntersectByExample() - { - Console.WriteLine("IntersectBy:"); - - // - Planet[] firstFivePlanetsFromTheSun = - [ - Planet.Mercury, - Planet.Venus, - Planet.Earth, - Planet.Mars, - Planet.Jupiter - ]; - - Planet[] lastFivePlanetsFromTheSun = - [ - Planet.Mars, - Planet.Jupiter, - Planet.Saturn, - Planet.Uranus, - Planet.Neptune - ]; - // - - // - foreach (Planet planet in - firstFivePlanetsFromTheSun.IntersectBy( - lastFivePlanetsFromTheSun, planet => planet)) - { - Console.WriteLine(planet); - } - - // This code produces the following output: - // Planet { Name = Mars, Type = Rock, OrderFromSun = 4 } - // Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 } - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.UnionBy.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.UnionBy.cs deleted file mode 100644 index aee4313821554..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.UnionBy.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace SolarSystem; - -public static partial class Program -{ - internal static void UnionByExample() - { - Console.WriteLine("UnionBy:"); - - // - Planet[] firstFivePlanetsFromTheSun = - [ - Planet.Mercury, - Planet.Venus, - Planet.Earth, - Planet.Mars, - Planet.Jupiter - ]; - - Planet[] lastFivePlanetsFromTheSun = - [ - Planet.Mars, - Planet.Jupiter, - Planet.Saturn, - Planet.Uranus, - Planet.Neptune - ]; - // - - // - foreach (Planet planet in - firstFivePlanetsFromTheSun.UnionBy( - lastFivePlanetsFromTheSun, planet => planet)) - { - Console.WriteLine(planet); - } - - // This code produces the following output: - // Planet { Name = Mercury, Type = Rock, OrderFromSun = 1 } - // Planet { Name = Venus, Type = Rock, OrderFromSun = 2 } - // Planet { Name = Earth, Type = Rock, OrderFromSun = 3 } - // Planet { Name = Mars, Type = Rock, OrderFromSun = 4 } - // Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 } - // Planet { Name = Saturn, Type = Gas, OrderFromSun = 6 } - // Planet { Name = Uranus, Type = Liquid, OrderFromSun = 7 } - // Planet { Name = Neptune, Type = Liquid, OrderFromSun = 8 } - // - - Console.WriteLine(); - } -} diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.cs b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.cs deleted file mode 100644 index 4de2dc65d3afc..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/Program.cs +++ /dev/null @@ -1,5 +0,0 @@ - -SolarSystem.Program.DistinctByExample(); -SolarSystem.Program.ExceptByExample(); -SolarSystem.Program.IntersectByExample(); -SolarSystem.Program.UnionByExample(); diff --git a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/set-operators.csproj b/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/set-operators.csproj deleted file mode 100644 index ddb1b95b31479..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/snippets/set-operators/set-operators.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - true - - - diff --git a/docs/csharp/programming-guide/concepts/linq/sorting-data.md b/docs/csharp/programming-guide/concepts/linq/sorting-data.md deleted file mode 100644 index b2470548170ae..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/sorting-data.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: "Sorting Data (C#)" -description: Learn about sort operations and the standard query operator methods that perform sort operations in LINQ in C#. -ms.date: 07/20/2015 -ms.assetid: d93fa055-2f19-46d2-9898-e2aed628f1c9 ---- -# Sorting Data (C#) - -A sorting operation orders the elements of a sequence based on one or more attributes. The first sort criterion performs a primary sort on the elements. By specifying a second sort criterion, you can sort the elements within each primary sort group. - - The following illustration shows the results of an alphabetical sort operation on a sequence of characters: - - ![Graphic that shows an alphabetical sort operation.](./media/sorting-data/alphabetical-sort-operation.png) - - The standard query operator methods that sort data are listed in the following section. - -## Methods - -|Method Name|Description|C# Query Expression Syntax|More Information| -|-----------------|-----------------|---------------------------------|----------------------| -|OrderBy|Sorts values in ascending order.|`orderby`|

| -|OrderByDescending|Sorts values in descending order.|`orderby … descending`|

| -|ThenBy|Performs a secondary sort in ascending order.|`orderby …, …`|

| -|ThenByDescending|Performs a secondary sort in descending order.|`orderby …, … descending`|

| -|Reverse|Reverses the order of the elements in a collection.|Not applicable.|

| - -## Query Expression Syntax Examples - -### Primary Sort Examples - -#### Primary Ascending Sort - - The following example demonstrates how to use the `orderby` clause in a LINQ query to sort the strings in an array by string length, in ascending order. - -```csharp -string[] words = ["the", "quick", "brown", "fox", "jumps"]; - -IEnumerable query = from word in words - orderby word.Length - select word; - -foreach (string str in query) - Console.WriteLine(str); - -/* This code produces the following output: - - the - fox - quick - brown - jumps -*/ -``` - -#### Primary Descending Sort - - The next example demonstrates how to use the `orderby descending` clause in a LINQ query to sort the strings by their first letter, in descending order. - -```csharp -string[] words = ["the", "quick", "brown", "fox", "jumps"]; - -IEnumerable query = from word in words - orderby word.Substring(0, 1) descending - select word; - -foreach (string str in query) - Console.WriteLine(str); - -/* This code produces the following output: - - the - quick - jumps - fox - brown -*/ -``` - -### Secondary Sort Examples - -#### Secondary Ascending Sort - - The following example demonstrates how to use the `orderby` clause in a LINQ query to perform a primary and secondary sort of the strings in an array. The strings are sorted primarily by length and secondarily by the first letter of the string, both in ascending order. - -```csharp -string[] words = ["the", "quick", "brown", "fox", "jumps"]; - -IEnumerable query = from word in words - orderby word.Length, word.Substring(0, 1) - select word; - -foreach (string str in query) - Console.WriteLine(str); - -/* This code produces the following output: - - fox - the - brown - jumps - quick -*/ -``` - -#### Secondary Descending Sort - - The next example demonstrates how to use the `orderby descending` clause in a LINQ query to perform a primary sort, in ascending order, and a secondary sort, in descending order. The strings are sorted primarily by length and secondarily by the first letter of the string. - -```csharp -string[] words = ["the", "quick", "brown", "fox", "jumps"]; - -IEnumerable query = from word in words - orderby word.Length, word.Substring(0, 1) descending - select word; - -foreach (string str in query) - Console.WriteLine(str); - -/* This code produces the following output: - - the - fox - quick - jumps - brown -*/ -``` - -## See also - -- -- [Standard Query Operators Overview (C#)](./standard-query-operators-overview.md) -- [orderby clause](../../../language-reference/keywords/orderby-clause.md) -- [Order the results of a join clause](../../../linq/order-the-results-of-a-join-clause.md) -- [How to sort or filter text data by any word or field (LINQ) (C#)](./how-to-sort-or-filter-text-data-by-any-word-or-field-linq.md) diff --git a/docs/csharp/programming-guide/concepts/linq/standard-query-operators-overview.md b/docs/csharp/programming-guide/concepts/linq/standard-query-operators-overview.md deleted file mode 100644 index 072a9004cfd5b..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/standard-query-operators-overview.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: "Standard Query Operators Overview (C#)" -description: The LINQ standard query operators provide query capabilities including filtering, projection, aggregation, and sorting in C#. -ms.date: 07/15/2023 -ms.assetid: 812fa119-5f65-4139-b4fa-55dccd8dc3ac ---- -# Standard Query Operators Overview (C#) - -The *standard query operators* are the methods that form the LINQ pattern. Most of these methods operate on sequences, where a sequence is an object whose type implements the interface or the interface. The standard query operators provide query capabilities including filtering, projection, aggregation, sorting and more. - - There are two sets of LINQ standard query operators: one that operates on objects of type , another that operates on objects of type . The methods that make up each set are static members of the and classes, respectively. They are defined as *extension methods* of the type that they operate on. Extension methods can be called by using either static method syntax or instance method syntax. - - In addition, several standard query operator methods operate on types other than those based on or . The type defines two such methods that both operate on objects of type . These methods, and , let you enable a non-parameterized, or non-generic, collection to be queried in the LINQ pattern. They do this by creating a strongly typed collection of objects. The class defines two similar methods, and , that operate on objects of type . - - The standard query operators differ in the timing of their execution, depending on whether they return a singleton value or a sequence of values. Those methods that return a singleton value (for example, and ) execute immediately. Methods that return a sequence defer the query execution and return an enumerable object. - - For methods that operate on in-memory collections, that is, those methods that extend , the returned enumerable object captures the arguments that were passed to the method. When that object is enumerated, the logic of the query operator is employed and the query results are returned. - - In contrast, methods that extend don't implement any querying behavior. They build an expression tree that represents the query to be performed. The query processing is handled by the source object. - - Calls to query methods can be chained together in one query, which enables queries to become arbitrarily complex. - - The following code example demonstrates how the standard query operators can be used to obtain information about a sequence. - -```csharp -string sentence = "the quick brown fox jumps over the lazy dog"; -// Split the string into individual words to create a collection. -string[] words = sentence.Split(' '); - -// Using query expression syntax. -var query = from word in words - group word.ToUpper() by word.Length into gr - orderby gr.Key - select new { Length = gr.Key, Words = gr }; - -// Using method-based query syntax. -var query2 = words. - GroupBy(w => w.Length, w => w.ToUpper()). - Select(g => new { Length = g.Key, Words = g }). - OrderBy(o => o.Length); - -foreach (var obj in query) -{ - Console.WriteLine("Words of length {0}:", obj.Length); - foreach (string word in obj.Words) - Console.WriteLine(word); -} - -// This code example produces the following output: -// -// Words of length 3: -// THE -// FOX -// THE -// DOG -// Words of length 4: -// OVER -// LAZY -// Words of length 5: -// QUICK -// BROWN -// JUMPS -``` - -## Query expression syntax - - Some of the more frequently used standard query operators have dedicated C# and Visual Basic language keyword syntax that enables them to be called as part of a *query* *expression*. For more information about standard query operators that have dedicated keywords and their corresponding syntaxes, see [Query Expression Syntax for Standard Query Operators (C#)](standard-query-operators-overview.md). - -## Extending the standard query operators - - You can augment the set of standard query operators by creating domain-specific methods that are appropriate for your target domain or technology. You can also replace the standard query operators with your own implementations that provide additional services such as remote evaluation, query translation, and optimization. See for an example. - -## Obtain a data source - - In a LINQ query, the first step is to specify the data source. In C# as in most programming languages a variable must be declared before it can be used. In a LINQ query, the `from` clause comes first in order to introduce the data source (`customers`) and the *range variable* (`cust`). - - [!code-csharp[csLINQGettingStarted#23](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#23)] - - The range variable is like the iteration variable in a `foreach` loop except that no actual iteration occurs in a query expression. When the query is executed, the range variable will serve as a reference to each successive element in `customers`. Because the compiler can infer the type of `cust`, you do not have to specify it explicitly. Additional range variables can be introduced by a `let` clause. For more information, see [let clause](../../../language-reference/keywords/let-clause.md). - -> [!NOTE] -> For non-generic data sources such as , the range variable must be explicitly typed. For more information, see [How to query an ArrayList with LINQ (C#)](./how-to-query-an-arraylist-with-linq.md) and [from clause](../../../language-reference/keywords/from-clause.md). - -## Filtering - - Probably the most common query operation is to apply a filter in the form of a Boolean expression. The filter causes the query to return only those elements for which the expression is true. The result is produced by using the `where` clause. The filter in effect specifies which elements to exclude from the source sequence. In the following example, only those `customers` who have an address in London are returned. - - [!code-csharp[csLINQGettingStarted#24](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#24)] - - You can use the familiar C# logical `AND` and `OR` operators to apply as many filter expressions as necessary in the `where` clause. For example, to return only customers from "London" `AND` whose name is "Devon" you would write the following code: - - [!code-csharp[csLINQGettingStarted#25](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#25)] - - To return customers from London or Paris, you would write the following code: - - [!code-csharp[csLINQGettingStarted#26](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#26)] - - For more information, see [where clause](../../../language-reference/keywords/where-clause.md). - -## Ordering - - Often it is convenient to sort the returned data. The `orderby` clause will cause the elements in the returned sequence to be sorted according to the default comparer for the type being sorted. For example, the following query can be extended to sort the results based on the `Name` property. Because `Name` is a string, the default comparer performs an alphabetical sort from A to Z. - - [!code-csharp[csLINQGettingStarted#27](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#27)] - - To order the results in reverse order, from Z to A, use the `orderby…descending` clause. - - For more information, see [orderby clause](../../../language-reference/keywords/orderby-clause.md). - -## Grouping - - The `group` clause enables you to group your results based on a key that you specify. For example you could specify that the results should be grouped by the `City` so that all customers from London or Paris are in individual groups. In this case, `cust.City` is the key. - - [!code-csharp[csLINQGettingStarted#28](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#28)] - - When you end a query with a `group` clause, your results take the form of a list of lists. Each element in the list is an object that has a `Key` member and a list of elements that are grouped under that key. When you iterate over a query that produces a sequence of groups, you must use a nested `foreach` loop. The outer loop iterates over each group, and the inner loop iterates over each group's members. - - If you must refer to the results of a group operation, you can use the `into` keyword to create an identifier that can be queried further. The following query returns only those groups that contain more than two customers: - - [!code-csharp[csLINQGettingStarted#29](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#29)] - - For more information, see [group clause](../../../language-reference/keywords/group-clause.md). - -## Joining - - Join operations create associations between sequences that are not explicitly modeled in the data sources. For example you can perform a join to find all the customers and distributors who have the same location. In LINQ the `join` clause always works against object collections instead of database tables directly. - - [!code-csharp[csLINQGettingStarted#36](~/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs#36)] - - In LINQ, you do not have to use `join` as often as you do in SQL, because foreign keys in LINQ are represented in the object model as properties that hold a collection of items. For example, a `Customer` object contains a collection of `Order` objects. Rather than performing a join, you access the orders by using dot notation: - -```csharp -from order in Customer.Orders... -``` - - For more information, see [join clause](../../../language-reference/keywords/join-clause.md). - -## Selecting (Projections) - - The `select` clause produces the results of the query and specifies the "shape" or type of each returned element. For example, you can specify whether your results will consist of complete `Customer` objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When the `select` clause produces something other than a copy of the source element, the operation is called a *projection*. The use of projections to transform data is a powerful capability of LINQ query expressions. For more information, see [Data Transformations with LINQ (C#)](./data-transformations-with-linq.md) and [select clause](../../../language-reference/keywords/select-clause.md). - -## Query Expression Syntax Table - - The following table lists the standard query operators that have equivalent query expression clauses. - -| Method | C# query expression syntax | -|------------|---------------------------------| -||Use an explicitly typed range variable, for example:

`from int i in numbers`

(For more information, see [from clause](../../../language-reference/keywords/from-clause.md).)| -||`group … by`

-or-

`group … by … into …`

(For more information, see [group clause](../../../language-reference/keywords/group-clause.md).)| -||`join … in … on … equals … into …`

(For more information, see [join clause](../../../language-reference/keywords/join-clause.md).)| -||`join … in … on … equals …`

(For more information, see [join clause](../../../language-reference/keywords/join-clause.md).)| -||`orderby`

(For more information, see [orderby clause](../../../language-reference/keywords/orderby-clause.md).)| -||`orderby … descending`

(For more information, see [orderby clause](../../../language-reference/keywords/orderby-clause.md).)| -||`select`

(For more information, see [select clause](../../../language-reference/keywords/select-clause.md).)| -||Multiple `from` clauses.

(For more information, see [from clause](../../../language-reference/keywords/from-clause.md).)| -||`orderby …, …`

(For more information, see [orderby clause](../../../language-reference/keywords/orderby-clause.md).)| -||`orderby …, … descending`

(For more information, see [orderby clause](../../../language-reference/keywords/orderby-clause.md).)| -||`where`

(For more information, see [where clause](../../../language-reference/keywords/where-clause.md).)| - -## See also - -- -- -- [Extension Methods](../../classes-and-structs/extension-methods.md) -- [Query Keywords (LINQ)](../../../language-reference/keywords/query-keywords.md) -- [Anonymous Types](../../../fundamentals/types/anonymous-types.md) diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index a44fa7ff3372e..f9576ab172e09 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -221,63 +221,25 @@ items: - name: Standard query operators items: - name: Overview - href: programming-guide/concepts/linq/standard-query-operators-overview.md - - name: Data Transformations with LINQ - href: programming-guide/concepts/linq/data-transformations-with-linq.md - - name: Store the results of a query in memory - href: linq/store-the-results-of-a-query-in-memory.md + href: linq/standard-query-operators/index.md - name: Filtering Data - href: programming-guide/concepts/linq/filtering-data.md + href: linq/standard-query-operators/filtering-data.md - name: Projection Operations - href: programming-guide/concepts/linq/projection-operations.md + href: linq/standard-query-operators/projection-operations.md - name: Set Operations - href: programming-guide/concepts/linq/set-operations.md + href: linq/standard-query-operators/set-operations.md - name: Sorting Data - href: programming-guide/concepts/linq/sorting-data.md + href: linq/standard-query-operators/sorting-data.md - name: Quantifier Operations - href: programming-guide/concepts/linq/quantifier-operations.md + href: linq/standard-query-operators/quantifier-operations.md - name: Partitioning Data - href: programming-guide/concepts/linq/partitioning-data.md - - name: Generation Operations - href: programming-guide/concepts/linq/generation-operations.md - - name: Equality Operations - href: programming-guide/concepts/linq/equality-operations.md - - name: Element Operations - href: programming-guide/concepts/linq/element-operations.md + href: linq/standard-query-operators/partitioning-data.md - name: Converting Data Types - href: programming-guide/concepts/linq/converting-data-types.md - - name: Concatenation Operations - href: programming-guide/concepts/linq/concatenation-operations.md - - name: Aggregation Operations - href: programming-guide/concepts/linq/aggregation-operations.md + href: linq/standard-query-operators/converting-data-types.md - name: Join Operations - items: - - name: Overview - href: programming-guide/concepts/linq/join-operations.md - - name: Perform inner joins - href: linq/perform-inner-joins.md - - name: Perform grouped joins - href: linq/perform-grouped-joins.md - - name: Perform left outer joins - href: linq/perform-left-outer-joins.md - - name: Order the results of a join clause - href: linq/order-the-results-of-a-join-clause.md - - name: Join by using composite keys - href: linq/join-by-using-composite-keys.md - - name: Perform custom join operations - href: linq/perform-custom-join-operations.md + href: linq/standard-query-operators/join-operations.md - name: Grouping Data - items: - - name: Overview - href: programming-guide/concepts/linq/grouping-data.md - - name: Group query results - href: linq/group-query-results.md - - name: Create a nested group - href: linq/create-a-nested-group.md - - name: Perform a subquery on a grouping operation - href: linq/perform-a-subquery-on-a-grouping-operation.md - - name: Group results by contiguous keys - href: linq/group-results-by-contiguous-keys.md + href: linq/standard-query-operators/grouping-data.md - name: LINQ to objects href: linq/query-a-collection-of-objects.md - name: "Walkthrough: Writing Queries in C# (LINQ)" @@ -340,14 +302,8 @@ items: href: programming-guide/concepts/linq/how-to-query-an-arraylist-with-linq.md - name: "How to add custom methods for LINQ queries" href: programming-guide/concepts/linq/how-to-add-custom-methods-for-linq-queries.md - - name: Supplemental API remarks - items: - - name: The BinaryExpression class - href: ../fundamentals/runtime-libraries/system-linq-expressions-binaryexpression.md - - name: The Expression class - items: - - name: Add method - href: ../fundamentals/runtime-libraries/system-linq-expressions-expression-add.md + - name: How to group results by contiguous keys + href: linq/group-results-by-contiguous-keys.md - name: Asynchronous programming items: - name: Overview @@ -469,6 +425,10 @@ items: href: advanced-topics/expression-trees/debugging-expression-trees-in-visual-studio.md - name: DebugView Syntax href: advanced-topics/expression-trees/debugview-syntax.md + - name: Supplemental API - Expression class add method + href: ../fundamentals/runtime-libraries/system-linq-expressions-expression-add.md + - name: Supplemental API - BinaryExpression class + href: ../fundamentals/runtime-libraries/system-linq-expressions-binaryexpression.md - name: Native interoperability items: - name: Overview diff --git a/docs/csharp/tutorials/working-with-linq.md b/docs/csharp/tutorials/working-with-linq.md index 568957abdbbb4..2e45256015957 100644 --- a/docs/csharp/tutorials/working-with-linq.md +++ b/docs/csharp/tutorials/working-with-linq.md @@ -346,7 +346,7 @@ Aside from LINQ, you learned a bit about a technique magicians use for card tric For more information on LINQ, see: - [Introduction to LINQ](/dotnet/csharp/linq/) -- [Basic LINQ Query Operations (C#)](../programming-guide/concepts/linq/standard-query-operators-overview.md) -- [Data Transformations With LINQ (C#)](../programming-guide/concepts/linq/data-transformations-with-linq.md) +- [Basic LINQ Query Operations (C#)](../linq/standard-query-operators/index.md) +- [Data Transformations With LINQ (C#)](../linq/standard-query-operators/index.md) - [Query Syntax and Method Syntax in LINQ (C#)](../linq/get-started/write-linq-queries.md) - [C# Features That Support LINQ](../linq/get-started/features-that-support-linq.md) diff --git a/docs/framework/data/adonet/linq-to-dataset-examples.md b/docs/framework/data/adonet/linq-to-dataset-examples.md index ea5e4d2efec60..d1b4c49d0dc20 100644 --- a/docs/framework/data/adonet/linq-to-dataset-examples.md +++ b/docs/framework/data/adonet/linq-to-dataset-examples.md @@ -6,7 +6,7 @@ ms.assetid: dfd91658-8d8a-45a4-a356-e327e809f21d --- # LINQ to DataSet Examples -This section provides LINQ to DataSet programming examples that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). +This section provides LINQ to DataSet programming examples that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/linq/standard-query-operators/index.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). ## In This Section diff --git a/docs/framework/data/adonet/method-based-query-examples-linq-to-dataset.md b/docs/framework/data/adonet/method-based-query-examples-linq-to-dataset.md index 05faec29f643d..0c148c5b1ed9c 100644 --- a/docs/framework/data/adonet/method-based-query-examples-linq-to-dataset.md +++ b/docs/framework/data/adonet/method-based-query-examples-linq-to-dataset.md @@ -6,7 +6,7 @@ ms.assetid: d340775c-7f39-4087-a290-5cbec6cfa68e --- # Method-Based Query Examples (LINQ to DataSet) -This section provides LINQ to DataSet programming examples in method-based query syntax that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). +This section provides LINQ to DataSet programming examples in method-based query syntax that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/linq/standard-query-operators/index.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). ## In This Section diff --git a/docs/framework/data/adonet/method-based-query-syntax-examples-aggregate-operators.md b/docs/framework/data/adonet/method-based-query-syntax-examples-aggregate-operators.md index d42f64d683474..b7a4fa1cdca15 100644 --- a/docs/framework/data/adonet/method-based-query-syntax-examples-aggregate-operators.md +++ b/docs/framework/data/adonet/method-based-query-syntax-examples-aggregate-operators.md @@ -165,5 +165,5 @@ The examples in this topic demonstrate how to use the method to query a using the method query syntax. @@ -44,5 +44,5 @@ Joining is an important operation in queries that target data sources that have - [Loading Data Into a DataSet](loading-data-into-a-dataset.md) - [LINQ to DataSet Examples](linq-to-dataset-examples.md) -- [Standard Query Operators Overview (C#)](../../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview (C#)](../../../csharp/linq/standard-query-operators/index.md) - [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md) diff --git a/docs/framework/data/adonet/method-based-query-syntax-examples-ordering-linq-to-dataset.md b/docs/framework/data/adonet/method-based-query-syntax-examples-ordering-linq-to-dataset.md index cf7d7ae5d8848..afbc27a799f94 100644 --- a/docs/framework/data/adonet/method-based-query-syntax-examples-ordering-linq-to-dataset.md +++ b/docs/framework/data/adonet/method-based-query-syntax-examples-ordering-linq-to-dataset.md @@ -53,5 +53,5 @@ The examples in this topic demonstrate how to use the to return all the rows from `Product` table and display the product names. diff --git a/docs/framework/data/adonet/query-expression-examples-linq-to-dataset.md b/docs/framework/data/adonet/query-expression-examples-linq-to-dataset.md index eaaf97e1fd3d0..ebac31a11234a 100644 --- a/docs/framework/data/adonet/query-expression-examples-linq-to-dataset.md +++ b/docs/framework/data/adonet/query-expression-examples-linq-to-dataset.md @@ -6,7 +6,7 @@ ms.assetid: f743fbc7-faff-45e5-af1e-61577d87f0cc --- # Query Expression Examples (LINQ to DataSet) -This section provides LINQ to DataSet programming examples in query expression syntax that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). +This section provides LINQ to DataSet programming examples in query expression syntax that use the standard query operators. The used in these examples is populated by using the `FillDataSet` method, which is specified in [Loading Data Into a DataSet](loading-data-into-a-dataset.md). For more information, see [Standard Query Operators Overview (C#)](../../../csharp/linq/standard-query-operators/index.md) or [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). ## In This Section diff --git a/docs/framework/data/adonet/query-expression-syntax-examples-aggregate-operators.md b/docs/framework/data/adonet/query-expression-syntax-examples-aggregate-operators.md index 2a39da71b0695..517db9396dfe5 100644 --- a/docs/framework/data/adonet/query-expression-syntax-examples-aggregate-operators.md +++ b/docs/framework/data/adonet/query-expression-syntax-examples-aggregate-operators.md @@ -106,5 +106,5 @@ The examples in this topic demonstrate how to use the and methods to query a using the query expression syntax. @@ -53,5 +53,5 @@ Joining is an important operation in queries that target data sources that have - [Loading Data Into a DataSet](loading-data-into-a-dataset.md) - [LINQ to DataSet Examples](linq-to-dataset-examples.md) -- [Standard Query Operators Overview (C#)](../../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) +- [Standard Query Operators Overview (C#)](../../../csharp/linq/standard-query-operators/index.md) - [Standard Query Operators Overview (Visual Basic)](../../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md) diff --git a/docs/framework/data/adonet/query-expression-syntax-examples-ordering-linq-to-dataset.md b/docs/framework/data/adonet/query-expression-syntax-examples-ordering-linq-to-dataset.md index bf83ab11d9534..21446f6b2d9a7 100644 --- a/docs/framework/data/adonet/query-expression-syntax-examples-ordering-linq-to-dataset.md +++ b/docs/framework/data/adonet/query-expression-syntax-examples-ordering-linq-to-dataset.md @@ -69,5 +69,5 @@ The examples in this topic demonstrate how to use the - - -- [Standard Query Operators Overview (C#)](../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) -- [Projection Operations (C#)](../../csharp/programming-guide/concepts/linq/projection-operations.md) +- [Standard Query Operators Overview (C#)](../../csharp/linq/standard-query-operators/index.md) +- [Projection Operations (C#)](../../csharp/linq/standard-query-operators/projection-operations.md) - [Basic Queries (LINQ to XML) (Visual Basic)](./find-element-specific-attribute.md) - [XML Child Axis Property (Visual Basic)](../../visual-basic/language-reference/xml-axis/xml-child-axis-property.md) - [XML Attribute Axis Property (Visual Basic)](../../visual-basic/language-reference/xml-axis/xml-attribute-axis-property.md) diff --git a/docs/standard/linq/find-element-specific-attribute.md b/docs/standard/linq/find-element-specific-attribute.md index e19c4dc161eff..837b0f6a3fe77 100644 --- a/docs/standard/linq/find-element-specific-attribute.md +++ b/docs/standard/linq/find-element-specific-attribute.md @@ -101,8 +101,8 @@ This example produces the following output:: - - -- [Standard Query Operators Overview (C#)](../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) -- [Projection Operations (C#)](../../csharp/programming-guide/concepts/linq/projection-operations.md) +- [Standard Query Operators Overview (C#)](../../csharp/linq/standard-query-operators/index.md) +- [Projection Operations (C#)](../../csharp/linq/standard-query-operators/projection-operations.md) - [Basic Queries (LINQ to XML) (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/basic-query-operations.md) - [Standard Query Operators Overview (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md) - [Projection Operations (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/projection-operations.md) diff --git a/docs/standard/linq/find-element-specific-child-element.md b/docs/standard/linq/find-element-specific-child-element.md index 222730d634aad..33d61f54969f8 100644 --- a/docs/standard/linq/find-element-specific-child-element.md +++ b/docs/standard/linq/find-element-specific-child-element.md @@ -91,5 +91,5 @@ This example produces the following output: - - -- [Standard Query Operators Overview](../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) -- [Projection Operations](../../csharp/programming-guide/concepts/linq/projection-operations.md) +- [Standard Query Operators Overview](../../csharp/linq/standard-query-operators/index.md) +- [Projection Operations](../../csharp/linq/standard-query-operators/projection-operations.md) diff --git a/docs/standard/linq/join-two-collections.md b/docs/standard/linq/join-two-collections.md index 07c80f1d847e3..94042a7abf314 100644 --- a/docs/standard/linq/join-two-collections.md +++ b/docs/standard/linq/join-two-collections.md @@ -18,7 +18,7 @@ The article [Sample XSD file: Customers and orders](sample-xsd-file-customers-or With LINQ to XML, you can take advantage of this relationship by using the `join` clause to join customer information to order information. -For more detailed information about `join`, see [Join Operations (C#)](../../csharp/programming-guide/concepts/linq/join-operations.md) and [Join Operations (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/join-operations.md). +For more detailed information about `join`, see [Join Operations (C#)](../../csharp/linq/standard-query-operators/join-operations.md) and [Join Operations (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/join-operations.md). > [!NOTE] > Joins are done using linear searches. There are no indexes to boost search performance. diff --git a/docs/standard/linq/refactor-pure-functions.md b/docs/standard/linq/refactor-pure-functions.md index 140aeed449d79..9c05ff83c7e2c 100644 --- a/docs/standard/linq/refactor-pure-functions.md +++ b/docs/standard/linq/refactor-pure-functions.md @@ -154,7 +154,7 @@ One approach that can be very useful is to write functions that are locally impu An important characteristic of the standard query operators is that they're implemented as pure functions. -For more information, see [Standard Query Operators Overview (C#)](../../csharp/programming-guide/concepts/linq/standard-query-operators-overview.md) and [Standard Query Operators Overview (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). +For more information, see [Standard Query Operators Overview (C#)](../../csharp/linq/standard-query-operators/index.md) and [Standard Query Operators Overview (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/standard-query-operators-overview.md). ## See also diff --git a/docs/standard/linq/remove-elements-attributes-nodes-xml-tree.md b/docs/standard/linq/remove-elements-attributes-nodes-xml-tree.md index ee163be38dda6..6e75bfba790a9 100644 --- a/docs/standard/linq/remove-elements-attributes-nodes-xml-tree.md +++ b/docs/standard/linq/remove-elements-attributes-nodes-xml-tree.md @@ -33,7 +33,7 @@ The following methods remove nodes and attributes from an XML tree. This example demonstrates three approaches to removing elements. First, it removes a single element. Second, it retrieves a collection of elements, materializes them using the operator, and then removes the collection. Finally, it retrieves a collection of elements and removes them using the extension method. -For more information on the operator, see [Converting Data Types (C#)](../../csharp/programming-guide/concepts/linq/converting-data-types.md) and [Converting Data Types (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/converting-data-types.md). +For more information on the operator, see [Converting Data Types (C#)](../../csharp/linq/standard-query-operators/converting-data-types.md) and [Converting Data Types (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/converting-data-types.md). ```csharp XElement root = XElement.Parse(@" diff --git a/docs/standard/linq/sort-elements.md b/docs/standard/linq/sort-elements.md index c7dc0ba9ae0fd..48634186067e4 100644 --- a/docs/standard/linq/sort-elements.md +++ b/docs/standard/linq/sort-elements.md @@ -101,6 +101,6 @@ This example produces the following output: ## See also -- [Sorting Data (C#)](../../csharp/programming-guide/concepts/linq/sorting-data.md) +- [Sorting Data (C#)](../../csharp/linq/standard-query-operators/sorting-data.md) - [Sorting Data (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/sorting-data.md) - [Basic Queries (LINQ to XML) (Visual Basic)](./find-element-specific-attribute.md) diff --git a/docs/standard/linq/write-queries-complex-filtering.md b/docs/standard/linq/write-queries-complex-filtering.md index a886c28f06391..2dadf866ee7dd 100644 --- a/docs/standard/linq/write-queries-complex-filtering.md +++ b/docs/standard/linq/write-queries-complex-filtering.md @@ -21,7 +21,7 @@ This example shows how to find all `PurchaseOrder` elements that have: It uses a nested query in the `Where` clause, and the `Any` operator returns `true` if the collection has any elements in it. The example uses XML document [Sample XML file: Multiple purchase orders](sample-xml-file-multiple-purchase-orders.md). -For more information about the `Any` operator, see [Quantifier Operations (C#)](../../../docs/csharp/programming-guide/concepts/linq/quantifier-operations.md) and [Quantifier Operations (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/quantifier-operations.md). +For more information about the `Any` operator, see [Quantifier Operations (C#)](../../csharp/linq/standard-query-operators/quantifier-operations.md) and [Quantifier Operations (Visual Basic)](../../visual-basic/programming-guide/concepts/linq/quantifier-operations.md). ```csharp XElement root = XElement.Load("PurchaseOrders.xml"); @@ -118,8 +118,8 @@ This example produces the following output: - - -- [Projection Operations (C#)](../../csharp/programming-guide/concepts/linq/projection-operations.md) -- [Quantifier Operations (C#)](../../csharp/programming-guide/concepts/linq/quantifier-operations.md) +- [Projection Operations (C#)](../../csharp/linq/standard-query-operators/projection-operations.md) +- [Quantifier Operations (C#)](../../csharp/linq/standard-query-operators/quantifier-operations.md) - [XML Child Axis Property (Visual Basic)](../../visual-basic/language-reference/xml-axis/xml-child-axis-property.md) - [XML Attribute Axis Property (Visual Basic)](../../visual-basic/language-reference/xml-axis/xml-attribute-axis-property.md) - [XML Value Property (Visual Basic)](../../visual-basic/language-reference/xml-axis/xml-value-property.md) diff --git a/docs/visual-basic/programming-guide/language-features/linq/how-to-combine-data-with-linq-by-using-joins.md b/docs/visual-basic/programming-guide/language-features/linq/how-to-combine-data-with-linq-by-using-joins.md index 63113dab88c07..c5da1d9bfec0f 100644 --- a/docs/visual-basic/programming-guide/language-features/linq/how-to-combine-data-with-linq-by-using-joins.md +++ b/docs/visual-basic/programming-guide/language-features/linq/how-to-combine-data-with-linq-by-using-joins.md @@ -87,4 +87,4 @@ Visual Basic provides the `Join` and `Group Join` query clauses to enable you to - [From Clause](../../../language-reference/queries/from-clause.md) - [Where Clause](../../../language-reference/queries/where-clause.md) - [Queries](../../../language-reference/queries/index.md) -- [Data Transformations with LINQ (C#)](../../../../csharp/programming-guide/concepts/linq/data-transformations-with-linq.md) +- [Data Transformations with LINQ (C#)](../../../../csharp/linq/standard-query-operators/index.md) diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs index 3953c8245a1ff..d18474078efd6 100644 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs +++ b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQGettingStarted/CS/Class1.cs @@ -4,591 +4,251 @@ using System.Text; using System.Xml.Linq; -namespace LINQGettingStarted_1 -{ - class Customer { } //so that Table compiles below - - class IntroToLINQ - { - static void Main() - { - } - } - - class DummyClass - { - //The Three Parts of a LINQ Query - static void CreateDataSources() - { - - } - } - - // - class Student - { - public string First { get; set; } - public string Last {get; set;} - public int ID { get; set; } - public string Street { get; set; } - public string City { get; set; } - public List Scores; - } +namespace LINQGettingStarted_1; - class Teacher +class Program //for the Walkthrough topic +{ + // + public class Student { public string First { get; set; } public string Last { get; set; } public int ID { get; set; } - public string City { get; set; } - } - // - - // - class DataTransformations - { - static void Main() - { - // Create the first data source. - List students = - [ - new Student { First="Svetlana", - Last="Omelchenko", - ID=111, - Street="123 Main Street", - City="Seattle", - Scores= new List { 97, 92, 81, 60 } }, - new Student { First="Claire", - Last="O’Donnell", - ID=112, - Street="124 Main Street", - City="Redmond", - Scores= new List { 75, 84, 91, 39 } }, - new Student { First="Sven", - Last="Mortensen", - ID=113, - Street="125 Main Street", - City="Lake City", - Scores= new List { 88, 94, 65, 91 } }, - ]; - - // Create the second data source. - List teachers = - [ - new Teacher { First="Ann", Last="Beebe", ID=945, City="Seattle" }, - new Teacher { First="Alex", Last="Robinson", ID=956, City="Redmond" }, - new Teacher { First="Michiyo", Last="Sato", ID=972, City="Tacoma" } - ]; - - // Create the query. - var peopleInSeattle = (from student in students - where student.City == "Seattle" - select student.Last) - .Concat(from teacher in teachers - where teacher.City == "Seattle" - select teacher.Last); - - Console.WriteLine("The following students and teachers live in Seattle:"); - // Execute the query. - foreach (var person in peopleInSeattle) - { - Console.WriteLine(person); - } - - } + public List Scores; } - /* Output: - The following students and teachers live in Seattle: - Omelchenko - Beebe - */ - // - // - class XMLTransform + // Create a data source by using a collection initializer. + static List students = + [ + new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List {97, 92, 81, 60}}, + new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List {75, 84, 91, 39}}, + new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List {88, 94, 65, 91}}, + new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List {97, 89, 85, 82}}, + new Student {First="Debra", Last="Garcia", ID=115, Scores= new List {35, 72, 91, 70}}, + new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List {99, 86, 90, 94}}, + new Student {First="Hanying", Last="Feng", ID=117, Scores= new List {93, 92, 80, 87}}, + new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List {92, 90, 83, 78}}, + new Student {First="Lance", Last="Tucker", ID=119, Scores= new List {68, 79, 88, 92}}, + new Student {First="Terry", Last="Adams", ID=120, Scores= new List {99, 82, 81, 79}}, + new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List {96, 85, 91, 60}}, + new Student {First="Michael", Last="Tucker", ID=122, Scores= new List {94, 92, 91, 91}} + ]; + // + + static void Main() { - static void Main() + // + // Create the query. + // The first line could also be written as "var studentQuery =" + IEnumerable studentQuery = + from student in students + where student.Scores[0] > 90 + select student; + // + + Console.WriteLine("\nExecuting Query 1............."); + // + // Execute the query. + // var could be used here also. + foreach (Student student in studentQuery) { - // Create the data source by using a collection initializer. - // The Student class was defined previously in this topic. - List students = - [ - new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List{97, 92, 81, 60}}, - new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List{75, 84, 91, 39}}, - new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List{88, 94, 65, 91}}, - ]; - - // Create the query. - var studentsToXML = new XElement("Root", - from student in students - let scores = string.Join(",", student.Scores) - select new XElement("student", - new XElement("First", student.First), - new XElement("Last", student.Last), - new XElement("Scores", scores) - ) // end "student" - ); // end "Root" - - // Execute the query. - Console.WriteLine(studentsToXML); - + Console.WriteLine("{0}, {1}", student.Last, student.First); } - } - // - /* Output: - - - Svetlana - Omelchenko - 97,92,81,60 - - - Claire - O'Donnell - 75,84,91,39 - - - Sven - Mortensen - 88,94,65,91 - - - */ - // - class FormatQuery - { - static void Main() + // Output: + // Omelchenko, Svetlana + // Garcia, Cesar + // Fakhouri, Fadi + // Feng, Hanying + // Garcia, Hugo + // Adams, Terry + // Zabokritski, Eugene + // Tucker, Michael + // + + // + IEnumerable> studentQuery2 = + from student in students + group student by student.Last[0]; + // + + Console.WriteLine("\nExecuting Query 2.............."); + // + foreach (IGrouping studentGroup in studentQuery2) { - // Data source. - double[] radii = [ 1, 2, 3 ]; - - // LINQ query using method syntax. - IEnumerable output = - radii.Select(r => $"Area for a circle with a radius of '{r}' = {r * r * Math.PI:F2}"); - - /* - // LINQ query using query syntax. - IEnumerable output = - from rad in radii - select $"Area for a circle with a radius of '{rad}' = {rad * rad * Math.PI:F2}"; - */ - - foreach (string s in output) + Console.WriteLine(studentGroup.Key); + foreach (Student student in studentGroup) { - Console.WriteLine(s); + Console.WriteLine(" {0}, {1}", + student.Last, student.First); } - } - } - /* Output: - Area for a circle with a radius of '1' = 3.14 - Area for a circle with a radius of '2' = 12.57 - Area for a circle with a radius of '3' = 28.27 - */ - // - - class Program //for the Walkthrough topic - { - // - public class Student - { - public string First { get; set; } - public string Last { get; set; } - public int ID { get; set; } - public List Scores; - } - - // Create a data source by using a collection initializer. - static List students = - [ - new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List {97, 92, 81, 60}}, - new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List {75, 84, 91, 39}}, - new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List {88, 94, 65, 91}}, - new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List {97, 89, 85, 82}}, - new Student {First="Debra", Last="Garcia", ID=115, Scores= new List {35, 72, 91, 70}}, - new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List {99, 86, 90, 94}}, - new Student {First="Hanying", Last="Feng", ID=117, Scores= new List {93, 92, 80, 87}}, - new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List {92, 90, 83, 78}}, - new Student {First="Lance", Last="Tucker", ID=119, Scores= new List {68, 79, 88, 92}}, - new Student {First="Terry", Last="Adams", ID=120, Scores= new List {99, 82, 81, 79}}, - new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List {96, 85, 91, 60}}, - new Student {First="Michael", Last="Tucker", ID=122, Scores= new List {94, 92, 91, 91}} - ]; - // - static void Main() + // Output: + // O + // Omelchenko, Svetlana + // O'Donnell, Claire + // M + // Mortensen, Sven + // G + // Garcia, Cesar + // Garcia, Debra + // Garcia, Hugo + // F + // Fakhouri, Fadi + // Feng, Hanying + // T + // Tucker, Lance + // Tucker, Michael + // A + // Adams, Terry + // Z + // Zabokritski, Eugene + // + + Console.WriteLine("\nExecuting Query 3.............."); + // + var studentQuery3 = + from student in students + group student by student.Last[0]; + + foreach (var groupOfStudents in studentQuery3) { - // - // Create the query. - // The first line could also be written as "var studentQuery =" - IEnumerable studentQuery = - from student in students - where student.Scores[0] > 90 - select student; - // - - Console.WriteLine("\nExecuting Query 1............."); - // - // Execute the query. - // var could be used here also. - foreach (Student student in studentQuery) - { - Console.WriteLine("{0}, {1}", student.Last, student.First); - } - - // Output: - // Omelchenko, Svetlana - // Garcia, Cesar - // Fakhouri, Fadi - // Feng, Hanying - // Garcia, Hugo - // Adams, Terry - // Zabokritski, Eugene - // Tucker, Michael - // - - // - IEnumerable> studentQuery2 = - from student in students - group student by student.Last[0]; - // - - Console.WriteLine("\nExecuting Query 2.............."); - // - foreach (IGrouping studentGroup in studentQuery2) + Console.WriteLine(groupOfStudents.Key); + foreach (var student in groupOfStudents) { - Console.WriteLine(studentGroup.Key); - foreach (Student student in studentGroup) - { - Console.WriteLine(" {0}, {1}", - student.Last, student.First); - } + Console.WriteLine(" {0}, {1}", + student.Last, student.First); } + } - // Output: - // O - // Omelchenko, Svetlana - // O'Donnell, Claire - // M - // Mortensen, Sven - // G - // Garcia, Cesar - // Garcia, Debra - // Garcia, Hugo - // F - // Fakhouri, Fadi - // Feng, Hanying - // T - // Tucker, Lance - // Tucker, Michael - // A - // Adams, Terry - // Z - // Zabokritski, Eugene - // - - Console.WriteLine("\nExecuting Query 3.............."); - // - var studentQuery3 = - from student in students - group student by student.Last[0]; - - foreach (var groupOfStudents in studentQuery3) - { - Console.WriteLine(groupOfStudents.Key); - foreach (var student in groupOfStudents) - { - Console.WriteLine(" {0}, {1}", - student.Last, student.First); - } - } - - // Output: - // O - // Omelchenko, Svetlana - // O'Donnell, Claire - // M - // Mortensen, Sven - // G - // Garcia, Cesar - // Garcia, Debra - // Garcia, Hugo - // F - // Fakhouri, Fadi - // Feng, Hanying - // T - // Tucker, Lance - // Tucker, Michael - // A - // Adams, Terry - // Z - // Zabokritski, Eugene - // - - Console.WriteLine("\nExecuting Query 4.............."); - // - var studentQuery4 = - from student in students - group student by student.Last[0] into studentGroup - orderby studentGroup.Key - select studentGroup; - - foreach (var groupOfStudents in studentQuery4) - { - Console.WriteLine(groupOfStudents.Key); - foreach (var student in groupOfStudents) - { - Console.WriteLine(" {0}, {1}", - student.Last, student.First); - } - } - - // Output: - //A - // Adams, Terry - //F - // Fakhouri, Fadi - // Feng, Hanying - //G - // Garcia, Cesar - // Garcia, Debra - // Garcia, Hugo - //M - // Mortensen, Sven - //O - // Omelchenko, Svetlana - // O'Donnell, Claire - //T - // Tucker, Lance - // Tucker, Michael - //Z - // Zabokritski, Eugene - // - - Console.WriteLine("\nExecuting Query 5.............."); - // - // studentQuery5 is an IEnumerable - // This query returns those students whose - // first test score was higher than their - // average score. - var studentQuery5 = - from student in students - let totalScore = student.Scores[0] + student.Scores[1] + - student.Scores[2] + student.Scores[3] - where totalScore / 4 < student.Scores[0] - select student.Last + " " + student.First; - - foreach (string s in studentQuery5) - { - Console.WriteLine(s); - } - - // Output: - // Omelchenko Svetlana - // O'Donnell Claire - // Mortensen Sven - // Garcia Cesar - // Fakhouri Fadi - // Feng Hanying - // Garcia Hugo - // Adams Terry - // Zabokritski Eugene - // Tucker Michael - // - - Console.WriteLine("\nExecuting Query 6.............."); - - // - var studentQuery6 = - from student in students - let totalScore = student.Scores[0] + student.Scores[1] + - student.Scores[2] + student.Scores[3] - select totalScore; - - double averageScore = studentQuery6.Average(); - Console.WriteLine("Class average score = {0}", averageScore); - - // Output: - // Class average score = 334.166666666667 - // - - Console.WriteLine("\nExecuting Query 7.............."); - // - IEnumerable studentQuery7 = - from student in students - where student.Last == "Garcia" - select student.First; - - Console.WriteLine("The Garcias in the class are:"); - foreach (string s in studentQuery7) - { - Console.WriteLine(s); - } - - // Output: - // The Garcias in the class are: - // Cesar - // Debra - // Hugo - // - - // - var studentQuery8 = - from student in students - let x = student.Scores[0] + student.Scores[1] + - student.Scores[2] + student.Scores[3] - where x > averageScore - select new { id = student.ID, score = x }; - - foreach (var item in studentQuery8) + // Output: + // O + // Omelchenko, Svetlana + // O'Donnell, Claire + // M + // Mortensen, Sven + // G + // Garcia, Cesar + // Garcia, Debra + // Garcia, Hugo + // F + // Fakhouri, Fadi + // Feng, Hanying + // T + // Tucker, Lance + // Tucker, Michael + // A + // Adams, Terry + // Z + // Zabokritski, Eugene + // + + Console.WriteLine("\nExecuting Query 4.............."); + // + var studentQuery4 = + from student in students + group student by student.Last[0] into studentGroup + orderby studentGroup.Key + select studentGroup; + + foreach (var groupOfStudents in studentQuery4) + { + Console.WriteLine(groupOfStudents.Key); + foreach (var student in groupOfStudents) { - Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score); + Console.WriteLine(" {0}, {1}", + student.Last, student.First); } - - // Output: - // Student ID: 113, Score: 338 - // Student ID: 114, Score: 353 - // Student ID: 116, Score: 369 - // Student ID: 117, Score: 352 - // Student ID: 118, Score: 343 - // Student ID: 120, Score: 341 - // Student ID: 122, Score: 368 - // } - // - // Lightweight class with auto-implemented properties - class NamePhone + // Output: + //A + // Adams, Terry + //F + // Fakhouri, Fadi + // Feng, Hanying + //G + // Garcia, Cesar + // Garcia, Debra + // Garcia, Hugo + //M + // Mortensen, Sven + //O + // Omelchenko, Svetlana + // O'Donnell, Claire + //T + // Tucker, Lance + // Tucker, Michael + //Z + // Zabokritski, Eugene + // + + Console.WriteLine("\nExecuting Query 5.............."); + // + // studentQuery5 is an IEnumerable + // This query returns those students whose + // first test score was higher than their + // average score. + var studentQuery5 = + from student in students + let totalScore = student.Scores[0] + student.Scores[1] + + student.Scores[2] + student.Scores[3] + where totalScore / 4 < student.Scores[0] + select student.Last + " " + student.First; + + foreach (string s in studentQuery5) { - public string Name { get; set; } - public string Phone{ get; set; } + Console.WriteLine(s); } - // - class BasicQueryOperations + // Output: + // Omelchenko Svetlana + // O'Donnell Claire + // Mortensen Sven + // Garcia Cesar + // Fakhouri Fadi + // Feng Hanying + // Garcia Hugo + // Adams Terry + // Zabokritski Eugene + // Tucker Michael + // + + Console.WriteLine("\nExecuting Query 6.............."); + + // + var studentQuery6 = + from student in students + let totalScore = student.Scores[0] + student.Scores[1] + + student.Scores[2] + student.Scores[3] + select totalScore; + + double averageScore = studentQuery6.Average(); + Console.WriteLine("Class average score = {0}", averageScore); + + // Output: + // Class average score = 334.166666666667 + // + + Console.WriteLine("\nExecuting Query 7.............."); + // + IEnumerable studentQuery7 = + from student in students + where student.Last == "Garcia" + select student.First; + + Console.WriteLine("The Garcias in the class are:"); + foreach (string s in studentQuery7) { - class Customer - { - public string City { get; set; } - public string Name { get; set; } - public string Phone {get; set; } - } - - public class Distributor - { - public string Name { get; set; } - public string City { get; set; } - public int ID { get; set; } - } - - static void Main() - { - List customers = new List(); - List distributors = new List(); - - // - //queryAllCustomers is an IEnumerable - var queryAllCustomers = from cust in customers - select cust; - // - - // - var queryLondonCustomers = from cust in customers - where cust.City == "London" - select cust; - // - - IEnumerable queryLondonCustomers2 = - from cust in customers - // - where cust.City == "London" && cust.Name == "Devon" - // - // - where cust.City == "London" || cust.City == "Paris" - // - select cust; - - // - var queryLondonCustomers3 = - from cust in customers - where cust.City == "London" - orderby cust.Name ascending - select cust; - // - - // - // queryCustomersByCity is an IEnumerable> - var queryCustomersByCity = - from cust in customers - group cust by cust.City; - - // customerGroup is an IGrouping - foreach (var customerGroup in queryCustomersByCity) - { - Console.WriteLine(customerGroup.Key); - foreach (Customer customer in customerGroup) - { - Console.WriteLine(" {0}", customer.Name); - } - } - // - - // - // custQuery is an IEnumerable> - var custQuery = - from cust in customers - group cust by cust.City into custGroup - where custGroup.Count() > 2 - orderby custGroup.Key - select custGroup; - // - - // - // queryNamesInLondon is an IEnumerable - var queryNamesInLondon = - from cust in customers - where cust.City == "London" - orderby cust.Name ascending - select cust.Name; - // - - //number 31 is the NamePhone struct before this class - // - // var could also be used here - IEnumerable queryLondonNamesPhones = - from cust in customers - where cust.City == "London" - orderby cust.Name ascending - select new NamePhone {Name = cust.Name, Phone = cust.Phone}; - // - - // - // var must be used here because of the anonymous type - var queryNamesPhones = - from cust in customers - where cust.City == "London" - orderby cust.Name ascending - select new { Name = cust.Name, Phone = cust.Phone }; - - foreach (var item in queryNamesPhones) - { - Console.WriteLine("{0} {1}", item.Name, item.Phone); - } - // - - // - var innerJoinQuery = - from cust in customers - join dist in distributors on cust.City equals dist.City - select new { CustomerName = cust.Name, DistributorName = dist.Name }; - // - } + Console.WriteLine(s); } - class LINQAndGenericTypes - { - - } + // Output: + // The Garcias in the class are: + // Cesar + // Debra + // Hugo + // } } diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Project.csproj b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Project.csproj deleted file mode 100644 index 60d266960cc58..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Project.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Library - net8.0 - enable - - diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs deleted file mode 100644 index b18e3e1a60c50..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQQuantifier/CS/Quantifier.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Sandbox -{ - class Program - { - static void Main(string[] args) - { - All.Example(); - Any.Example(); - Contains.Example(); - } - - static class All - { - // - class Market - { - public string Name { get; set; } - public string[] Items { get; set; } - } - - public static void Example() - { - List markets = - [ - new Market { Name = "Emily's", Items = ["kiwi", "cheery", "banana"] }, - new Market { Name = "Kim's", Items = ["melon", "mango", "olive"] }, - new Market { Name = "Adam's", Items = ["kiwi", "apple", "orange"] }, - ]; - - // Determine which market have all fruit names length equal to 5 - IEnumerable names = from market in markets - where market.Items.All(item => item.Length == 5) - select market.Name; - - foreach (string name in names) - { - Console.WriteLine($"{name} market"); - } - - // This code produces the following output: - // - // Kim's market - } - // - } - - static class Any - { - // - class Market - { - public string Name { get; set; } - public string[] Items { get; set; } - } - - public static void Example() - { - List markets = - [ - new Market { Name = "Emily's", Items = ["kiwi", "cheery", "banana"] }, - new Market { Name = "Kim's", Items = ["melon", "mango", "olive"] }, - new Market { Name = "Adam's", Items = ["kiwi", "apple", "orange"] }, - ]; - - // Determine which market have any fruit names start with 'o' - IEnumerable names = from market in markets - where market.Items.Any(item => item.StartsWith("o")) - select market.Name; - - foreach (string name in names) - { - Console.WriteLine($"{name} market"); - } - - // This code produces the following output: - // - // Kim's market - // Adam's market - } - // - } - - static class Contains - { - // - class Market - { - public string Name { get; set; } - public string[] Items { get; set; } - } - - public static void Example() - { - List markets = - [ - new Market { Name = "Emily's", Items = ["kiwi", "cheery", "banana"] }, - new Market { Name = "Kim's", Items = ["melon", "mango", "olive"] }, - new Market { Name = "Adam's", Items = ["kiwi", "apple", "orange"] }, - ]; - - // Determine which market contains fruit names equal 'kiwi' - IEnumerable names = from market in markets - where market.Items.Contains("kiwi") - select market.Name; - - foreach (string name in names) - { - Console.WriteLine($"{name} market"); - } - - // This code produces the following output: - // - // Emily's market - // Adam's market - } - // - } - } -} diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/Project.csproj b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/Project.csproj deleted file mode 100644 index 60d266960cc58..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/Project.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Library - net8.0 - enable - - diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs deleted file mode 100644 index 7eb4ac9ec293d..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/CsLINQSetOperation/CS/SetOperation.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Sandbox -{ - class Program - { - static void Main(string[] args) - { - Union(); - } - - private static void Distinct() - { - // - string[] planets = ["Mercury", "Venus", "Venus", "Earth", "Mars", "Earth"]; - - IEnumerable query = from planet in planets.Distinct() - select planet; - - foreach (var str in query) - { - Console.WriteLine(str); - } - - /* This code produces the following output: - * - * Mercury - * Venus - * Earth - * Mars - */ - // - } - - private static void Except() - { - // - string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; - string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; - - IEnumerable query = from planet in planets1.Except(planets2) - select planet; - - foreach (var str in query) - { - Console.WriteLine(str); - } - - /* This code produces the following output: - * - * Venus - */ - // - } - - private static void Intersect() - { - // - string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; - string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; - - IEnumerable query = from planet in planets1.Intersect(planets2) - select planet; - - foreach (var str in query) - { - Console.WriteLine(str); - } - - /* This code produces the following output: - * - * Mercury - * Earth - * Jupiter - */ - // - } - - private static void Union() - { - // - string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; - string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; - - IEnumerable query = from planet in planets1.Union(planets2) - select planet; - - foreach (var str in query) - { - Console.WriteLine(str); - } - - /* This code produces the following output: - * - * Mercury - * Venus - * Earth - * Jupiter - * Mars - */ - // - } - } -} diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/JoinOperation.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/JoinOperation.cs deleted file mode 100644 index ac3daed82d300..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/csLINQJoinOperation/CS/JoinOperation.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace Sandbox; - -class JoinOperation -{ - static void Main(string[] args) - { - Join.Example(); - } - - static class Join - { - // - class Product - { - public string? Name { get; set; } - public int CategoryId { get; set; } - } - - class Category - { - public int Id { get; set; } - public string? CategoryName { get; set; } - } - - public static void Example() - { - List products = - [ - new Product { Name = "Cola", CategoryId = 0 }, - new Product { Name = "Tea", CategoryId = 0 }, - new Product { Name = "Apple", CategoryId = 1 }, - new Product { Name = "Kiwi", CategoryId = 1 }, - new Product { Name = "Carrot", CategoryId = 2 }, - ]; - - List categories = - [ - new Category { Id = 0, CategoryName = "Beverage" }, - new Category { Id = 1, CategoryName = "Fruit" }, - new Category { Id = 2, CategoryName = "Vegetable" } - ]; - - // Join products and categories based on CategoryId - var query = from product in products - join category in categories on product.CategoryId equals category.Id - select new { product.Name, category.CategoryName }; - - foreach (var item in query) - { - Console.WriteLine($"{item.Name} - {item.CategoryName}"); - } - - // This code produces the following output: - // - // Cola - Beverage - // Tea - Beverage - // Apple - Fruit - // Kiwi - Fruit - // Carrot - Vegetable - } - // - } - - static class GroupJoin - { - // - class Product - { - public string? Name { get; set; } - public int CategoryId { get; set; } - } - - class Category - { - public int Id { get; set; } - public string? CategoryName { get; set; } - } - - public static void Example() - { - List products = - [ - new() { Name = "Cola", CategoryId = 0 }, - new() { Name = "Tea", CategoryId = 0 }, - new() { Name = "Apple", CategoryId = 1 }, - new() { Name = "Kiwi", CategoryId = 1 }, - new() { Name = "Carrot", CategoryId = 2 }, - ]; - - List categories = - [ - new() { Id = 0, CategoryName = "Beverage" }, - new() { Id = 1, CategoryName = "Fruit" }, - new() { Id = 2, CategoryName = "Vegetable" } - ]; - - // Join categories and product based on CategoryId and grouping result - var productGroups = from category in categories - join product in products on category.Id equals product.CategoryId into productGroup - select productGroup; - - foreach (IEnumerable productGroup in productGroups) - { - Console.WriteLine("Group"); - foreach (Product product in productGroup) - { - Console.WriteLine($"{product.Name,8}"); - } - } - - // This code produces the following output: - // - // Group - // Cola - // Tea - // Group - // Apple - // Kiwi - // Group - // Carrot - } - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/CustomJoinsTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/CustomJoinsTest.cs deleted file mode 100644 index f313fd4347789..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/CustomJoinsTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class CustomJoinsTest -{ - [Fact] - public void CustomJoins1Test() - { - StringWriter sw = InitTest(); - - CustomJoins.CustomJoins1(); - Assert.Equal( -@"Cross Join Query: -1 Tea -1 Mustard -1 Pickles -1 Carrots -1 Bok Choy -1 Peaches -1 Melons -1 Ice Cream -1 Mackerel -2 Tea -2 Mustard -2 Pickles -2 Carrots -2 Bok Choy -2 Peaches -2 Melons -2 Ice Cream -2 Mackerel -3 Tea -3 Mustard -3 Pickles -3 Carrots -3 Bok Choy -3 Peaches -3 Melons -3 Ice Cream -3 Mackerel -Non-equijoin query: -1 Tea -2 Mustard -2 Pickles -3 Carrots -3 Bok Choy -", sw.ToString()); - } - - [Fact] - public void MergeCsvFilesTest() - { - StringWriter sw = InitTest(); - - CustomJoins.MergeCsvFiles(); - Assert.Equal( -@"The average score of Omelchenko Svetlana is 82.5. -The average score of O'Donnell Claire is 72.25. -The average score of Mortensen Sven is 84.5. -The average score of Garcia Cesar is 88.25. -The average score of Garcia Debra is 67. -The average score of Fakhouri Fadi is 92.25. -The average score of Feng Hanying is 88. -The average score of Garcia Hugo is 85.75. -The average score of Tucker Lance is 81.75. -The average score of Adams Terry is 85.25. -The average score of Zabokritski Eugene is 83. -The average score of Tucker Michael is 92. -", sw.ToString()); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupJoinsTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupJoinsTest.cs deleted file mode 100644 index dcae5416b0a6e..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupJoinsTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class GroupJoinsTest -{ - [Fact] - public void GroupJoins1Test() - { - StringWriter sw = InitTest(); - - GroupJoins.GroupJoin1(); - Assert.Equal( -@"Magnus: - Daisy -Terry: - Barley - Boots - Blue Moon -Charlotte: - Whiskers -Arlene: -", sw.ToString()); - } - - [Fact] - public void GroupJoinXmlTest() - { - StringWriter sw = InitTest(); - - GroupJoins.GroupJoinXml(); - Assert.Equal( -@" - - Daisy - - - Barley - Boots - Blue Moon - - - Whiskers - - - -", sw.ToString()); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupQueryResultsTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupQueryResultsTest.cs deleted file mode 100644 index 0f4d54248449f..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/GroupQueryResultsTest.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class GroupQueryResultsTest -{ - [Fact] - public void GroupQueryResults1Test() - { - StringWriter sw = InitTest(); - - GroupQueryResults.GroupQueryResults1(); - Assert.Equal( -@"Key: Adams - Adams, Terry -Key: Fakhouri - Fakhouri, Fadi -Key: Feng - Feng, Hanying -Key: Garcia - Garcia, Cesar - Garcia, Debra - Garcia, Hugo -Key: Mortensen - Mortensen, Sven -Key: O'Donnell - O'Donnell, Claire -Key: Omelchenko - Omelchenko, Svetlana -Key: Tucker - Tucker, Lance - Tucker, Michael -Key: Zabokritski - Zabokritski, Eugene -", sw.ToString().Replace("\t", " ")); - } - - [Fact] - public void GroupQueryResults2Test() - { - StringWriter sw = InitTest(); - - GroupQueryResults.GroupQueryResults2(); - Assert.Equal( -@"Key: A - Adams, Terry -Key: F - Fakhouri, Fadi - Feng, Hanying -Key: G - Garcia, Cesar - Garcia, Debra - Garcia, Hugo -Key: M - Mortensen, Sven -Key: O - O'Donnell, Claire - Omelchenko, Svetlana -Key: T - Tucker, Lance - Tucker, Michael -Key: Z - Zabokritski, Eugene -", sw.ToString().Replace("\t", " ")); - } - - [Fact] - public void GroupQueryResults3Test() - { - StringWriter sw = InitTest(); - - GroupQueryResults.GroupQueryResults3(); - Assert.Equal( -@"Key: 60 - Garcia, Debra -Key: 70 - O'Donnell, Claire -Key: 80 - Adams, Terry - Feng, Hanying - Garcia, Cesar - Garcia, Hugo - Mortensen, Sven - Omelchenko, Svetlana - Tucker, Lance - Zabokritski, Eugene -Key: 90 - Fakhouri, Fadi - Tucker, Michael -", sw.ToString().Replace("\t", " ")); - } - - [Fact] - public void GroupQueryResults4Test() - { - StringWriter sw = InitTest(); - - GroupQueryResults.GroupQueryResults4(); - Assert.Equal( -@"Key: True - Terry Adams - Fadi Fakhouri - Hanying Feng - Cesar Garcia - Hugo Garcia - Sven Mortensen - Svetlana Omelchenko - Lance Tucker - Michael Tucker - Eugene Zabokritski -Key: False - Debra Garcia - Claire O'Donnell -", sw.ToString().Replace("\t", " ")); - } - - [Fact] - public void GroupQueryResults5Test() - { - StringWriter sw = InitTest(); - - GroupQueryResults.GroupQueryResults5(); - Assert.Equal( -@"Name starts with A who scored more than 85 - Terry Adams -Name starts with F who scored more than 85 - Fadi Fakhouri - Hanying Feng -Name starts with G who scored more than 85 - Cesar Garcia - Hugo Garcia -Name starts with G who scored less than 85 - Debra Garcia -Name starts with M who scored more than 85 - Sven Mortensen -Name starts with O who scored less than 85 - Claire O'Donnell -Name starts with O who scored more than 85 - Svetlana Omelchenko -Name starts with T who scored less than 85 - Lance Tucker -Name starts with T who scored more than 85 - Michael Tucker -Name starts with Z who scored more than 85 - Eugene Zabokritski -", sw.ToString().Replace("\t", " ")); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/InnerJoinsTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/InnerJoinsTest.cs deleted file mode 100644 index 80de5f7f71e85..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/InnerJoinsTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Xunit; - -namespace LinqSamples.Test; - -public class InnerJoinsTest -{ - [Fact] - public void BasicTest() - { - const string expectedOutput = -@"""Daisy"" is owned by Magnus -""Barley"" is owned by Terry -""Boots"" is owned by Terry -""Whiskers"" is owned by Charlotte -""Blue Moon"" is owned by Rui -"; - - var querySyntaxResult = InnerJoins.Basic(); - Assert.Equal(expectedOutput, querySyntaxResult); - - var methodSyntaxResult = InnerJoins.BasicMethodSyntax(); - Assert.Equal(methodSyntaxResult, querySyntaxResult); - } - - [Fact] - public void CompositeKeyTest() - { - const string expectedOutput = -@"The following people are both employees and students: -Terry Adams -Vernette Price -"; - var querySyntaxResult = InnerJoins.CompositeKey(); - Assert.Equal(expectedOutput, querySyntaxResult); - - var methodSyntaxResult = InnerJoins.CompositeKeyMethodSyntax(); - Assert.Equal(methodSyntaxResult, querySyntaxResult); - } - - [Fact] - public void MultipleJoinTest() - { - const string expectedOutput = -@"The cat ""Daisy"" shares a house, and the first letter of their name, with ""Duke"". -The cat ""Whiskers"" shares a house, and the first letter of their name, with ""Wiley"". -"; - var querySyntaxResult = InnerJoins.MultipleJoin(); - Assert.Equal(expectedOutput, querySyntaxResult); - - var methodSyntaxResult = InnerJoins.MultipleJoinMethodSyntax(); - Assert.Equal(methodSyntaxResult, querySyntaxResult); - } - - [Fact] - public void InnerGroupJoinTest() - { - const string expectedOutput = -@"Inner join using GroupJoin(): -Magnus - Daisy -Terry - Barley -Terry - Boots -Terry - Blue Moon -Charlotte - Whiskers - -The equivalent operation using Join(): -Magnus - Daisy -Terry - Barley -Terry - Boots -Terry - Blue Moon -Charlotte - Whiskers -"; - var querySyntaxResult = InnerJoins.InnerGroupJoin(); - Assert.Equal(expectedOutput, querySyntaxResult); - - var methodSyntaxResult = InnerJoins.InnerGroupJoinMethodSyntax(); - Assert.Equal(methodSyntaxResult, querySyntaxResult); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/LeftOuterJoinTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/LeftOuterJoinTest.cs deleted file mode 100644 index 20cc5b4d92a58..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/LeftOuterJoinTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class LeftOuterJoinTest -{ - [Fact] - public void LeftOuterJoin1Test() - { - StringWriter sw = InitTest(); - - LeftOuterJoin.LeftOuterJoin1(); - Assert.Equal( -@"Magnus: Daisy -Terry: Barley -Terry: Boots -Terry: Blue Moon -Charlotte: Whiskers -Arlene: -", sw.ToString()); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/NestedGroupsTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/NestedGroupsTest.cs deleted file mode 100644 index 5f191cd95fab7..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/NestedGroupsTest.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class NestedGroupsTest -{ - [Fact] - public void NestedGroups1Test() - { - StringWriter sw = InitTest(); - - NestedGroups.NestedGroup1(); - Assert.Equal( -@"DataClass.Student Level = SecondYear - Names that begin with: Adams - Adams Terry - Names that begin with: Garcia - Garcia Hugo - Names that begin with: Omelchenko - Omelchenko Svetlana -DataClass.Student Level = ThirdYear - Names that begin with: Fakhouri - Fakhouri Fadi - Names that begin with: Garcia - Garcia Debra - Names that begin with: Tucker - Tucker Lance -DataClass.Student Level = FirstYear - Names that begin with: Feng - Feng Hanying - Names that begin with: Mortensen - Mortensen Sven - Names that begin with: Tucker - Tucker Michael -DataClass.Student Level = FourthYear - Names that begin with: Garcia - Garcia Cesar - Names that begin with: O'Donnell - O'Donnell Claire - Names that begin with: Zabokritski - Zabokritski Eugene -", sw.ToString().Replace("\t", " ")); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/OrderResultsOfJoinTest.cs b/samples/snippets/csharp/concepts/linq/LinqSamples.Test/OrderResultsOfJoinTest.cs deleted file mode 100644 index 149130f9633a8..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples.Test/OrderResultsOfJoinTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Xunit; -using static LinqSamples.Test.Shared; - -namespace LinqSamples.Test; - -public class OrderResultsOfJoinTest -{ - [Fact] - public void OrderResultsOfJoinTest1() - { - StringWriter sw = InitTest(); - - OrderResultsOfJoin.OrderResultsOfJoin1(); - Assert.Equal( -@"Beverages - Cola 1 - Tea 1 -Condiments - Mustard 2 - Pickles 2 -Fruit - Melons 5 - Peaches 5 -Grains -Vegetables - Bok Choy 3 - Carrots 3 -", sw.ToString()); - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs deleted file mode 100644 index 2f18ea49e4972..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/CustomJoins.cs +++ /dev/null @@ -1,152 +0,0 @@ -using LinqSamples.Joins; - -namespace LinqSamples; - -public static class CustomJoins -{ - public static void CustomJoins1() - { - // - List categories = - [ - new(Name: "Beverages", ID: 001), - new("Condiments", 002), - new("Vegetables", 003) - ]; - - List products = - [ - new(Name: "Tea", CategoryID: 001), - new("Mustard", 002), - new("Pickles", 002), - new("Carrots", 003), - new("Bok Choy", 003), - new("Peaches", 005), - new("Melons", 005), - new("Ice Cream", 007), - new("Mackerel", 012) - ]; - - var crossJoinQuery = - from c in categories - from p in products - select new - { - c.ID, - p.Name - }; - - Console.WriteLine("Cross Join Query:"); - foreach (var v in crossJoinQuery) - { - Console.WriteLine($"{v.ID,-5}{v.Name}"); - } - /* Output: - Cross Join Query: - 1 Tea - 1 Mustard - 1 Pickles - 1 Carrots - 1 Bok Choy - 1 Peaches - 1 Melons - 1 Ice Cream - 1 Mackerel - 2 Tea - 2 Mustard - 2 Pickles - 2 Carrots - 2 Bok Choy - 2 Peaches - 2 Melons - 2 Ice Cream - 2 Mackerel - 3 Tea - 3 Mustard - 3 Pickles - 3 Carrots - 3 Bok Choy - 3 Peaches - 3 Melons - 3 Ice Cream - 3 Mackerel - */ - // - - // - var nonEquijoinQuery = - from p in products - let catIds = - from c in categories - select c.ID - where catIds.Contains(p.CategoryID) - select new - { - Product = p.Name, - p.CategoryID - }; - - Console.WriteLine("Non-equijoin query:"); - foreach (var v in nonEquijoinQuery) - { - Console.WriteLine($"{v.CategoryID,-5}{v.Product}"); - } - - /* Output: - Non-equijoin query: - 1 Tea - 2 Mustard - 2 Pickles - 3 Carrots - 3 Bok Choy - */ - // - } - - public static void MergeCsvFiles() - { - // - var names = File.ReadAllLines("csv/names.csv"); - var scores = File.ReadAllLines("csv/scores.csv"); - - IEnumerable queryNamesScores = - // Split each line in the data files into an array of strings. - from name in names - let x = name.Split(',') - from score in scores - let s = score.Split(',') - // Look for matching IDs from the two data files. - where x[2] == s[0] - // If the IDs match, build a Student object. - select new Student( - FirstName: x[0], - LastName: x[1], - StudentID: int.Parse(x[2]), - ExamScores: ( - from scoreAsText in s.Skip(1) - select int.Parse(scoreAsText) - ).ToList() - ); - - foreach (var student in queryNamesScores) - { - Console.WriteLine($"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}."); - } - - /* Output: - The average score of Omelchenko Svetlana is 82.5. - The average score of O'Donnell Claire is 72.25. - The average score of Mortensen Sven is 84.5. - The average score of Garcia Cesar is 88.25. - The average score of Garcia Debra is 67. - The average score of Fakhouri Fadi is 92.25. - The average score of Feng Hanying is 88. - The average score of Garcia Hugo is 85.75. - The average score of Tucker Lance is 81.75. - The average score of Adams Terry is 85.25. - The average score of Zabokritski Eugene is 83. - The average score of Tucker Michael is 92. - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs index 989352d6b6da6..23c816a13d5aa 100644 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs +++ b/samples/snippets/csharp/concepts/linq/LinqSamples/DataClasses.cs @@ -1,125 +1,101 @@ -namespace LinqSamples +namespace LinqSamples; + +// +class Student { - namespace QueryExpressionBasics - { - record City(long Population); - record Country(string Name, List Cities, long Area, long Population); - record Product(string Name, string Category); - } + public string FirstName { get; set; } + public string LastName { get; set; } + public int ID { get; set; } + public GradeLevel? Year { get; set; } + public List ExamScores { get; set; } - // - class Student + public Student(string FirstName, string LastName, int ID, GradeLevel Year, List ExamScores) { - public string FirstName { get; set; } - public string LastName { get; set; } - public int ID { get; set; } - public GradeLevel? Year { get; set; } - public List ExamScores { get; set; } - - public Student(string FirstName, string LastName, int ID, GradeLevel Year, List ExamScores) - { - this.FirstName = FirstName; - this.LastName = LastName; - this.ID = ID; - this.Year = Year; - this.ExamScores = ExamScores; - } - - public Student(string FirstName, string LastName, int StudentID, List? ExamScores = null) - { - this.FirstName = FirstName; - this.LastName = LastName; - ID = StudentID; - this.ExamScores = ExamScores ?? []; - } - - public static List students = - [ - new( - FirstName: "Terry", LastName: "Adams", ID: 120, - Year: GradeLevel.SecondYear, - ExamScores: [99, 82, 81, 79] - ), - new( - "Fadi", "Fakhouri", 116, - GradeLevel.ThirdYear, - [99, 86, 90, 94] - ), - new( - "Hanying", "Feng", 117, - GradeLevel.FirstYear, - [93, 92, 80, 87] - ), - new( - "Cesar", "Garcia", 114, - GradeLevel.FourthYear, - [97, 89, 85, 82] - ), - new( - "Debra", "Garcia", 115, - GradeLevel.ThirdYear, - [35, 72, 91, 70] - ), - new( - "Hugo", "Garcia", 118, - GradeLevel.SecondYear, - [92, 90, 83, 78] - ), - new( - "Sven", "Mortensen", 113, - GradeLevel.FirstYear, - [88, 94, 65, 91] - ), - new( - "Claire", "O'Donnell", 112, - GradeLevel.FourthYear, - [75, 84, 91, 39] - ), - new( - "Svetlana", "Omelchenko", 111, - GradeLevel.SecondYear, - [97, 92, 81, 60] - ), - new( - "Lance", "Tucker", 119, - GradeLevel.ThirdYear, - [68, 79, 88, 92] - ), - new( - "Michael", "Tucker", 122, - GradeLevel.FirstYear, - [94, 92, 91, 91] - ), - new( - "Eugene", "Zabokritski", 121, - GradeLevel.FourthYear, - [96, 85, 91, 60] - ) - ]; + this.FirstName = FirstName; + this.LastName = LastName; + this.ID = ID; + this.Year = Year; + this.ExamScores = ExamScores; } - enum GradeLevel + public Student(string FirstName, string LastName, int StudentID, List? ExamScores = null) { - FirstYear = 1, - SecondYear, - ThirdYear, - FourthYear - }; - // - - // - record Person(string FirstName, string LastName); - record Pet(string Name, Person Owner); - record Employee(string FirstName, string LastName, int EmployeeID); - record Cat(string Name, Person Owner) : Pet(Name, Owner); - record Dog(string Name, Person Owner) : Pet(Name, Owner); - // - - namespace Joins - { - // - record Product(string Name, int CategoryID); - record Category(string Name, int ID); - // + this.FirstName = FirstName; + this.LastName = LastName; + ID = StudentID; + this.ExamScores = ExamScores ?? []; } + + public static List students = + [ + new( + FirstName: "Terry", LastName: "Adams", ID: 120, + Year: GradeLevel.SecondYear, + ExamScores: [99, 82, 81, 79] + ), + new( + "Fadi", "Fakhouri", 116, + GradeLevel.ThirdYear, + [99, 86, 90, 94] + ), + new( + "Hanying", "Feng", 117, + GradeLevel.FirstYear, + [93, 92, 80, 87] + ), + new( + "Cesar", "Garcia", 114, + GradeLevel.FourthYear, + [97, 89, 85, 82] + ), + new( + "Debra", "Garcia", 115, + GradeLevel.ThirdYear, + [35, 72, 91, 70] + ), + new( + "Hugo", "Garcia", 118, + GradeLevel.SecondYear, + [92, 90, 83, 78] + ), + new( + "Sven", "Mortensen", 113, + GradeLevel.FirstYear, + [88, 94, 65, 91] + ), + new( + "Claire", "O'Donnell", 112, + GradeLevel.FourthYear, + [75, 84, 91, 39] + ), + new( + "Svetlana", "Omelchenko", 111, + GradeLevel.SecondYear, + [97, 92, 81, 60] + ), + new( + "Lance", "Tucker", 119, + GradeLevel.ThirdYear, + [68, 79, 88, 92] + ), + new( + "Michael", "Tucker", 122, + GradeLevel.FirstYear, + [94, 92, 91, 91] + ), + new( + "Eugene", "Zabokritski", 121, + GradeLevel.FourthYear, + [96, 85, 91, 60] + ) + ]; } + +enum GradeLevel +{ + FirstYear = 1, + SecondYear, + ThirdYear, + FourthYear +}; +// diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupJoins.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/GroupJoins.cs deleted file mode 100644 index c147dd05157f4..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupJoins.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Xml.Linq; - -namespace LinqSamples; - -public static class GroupJoins -{ - public static void GroupJoin1() - { - // - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - - List people = [magnus, terry, charlotte, arlene]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", terry), - new("Daisy", magnus), - ]; - - var query = - from person in people - join pet in pets on person equals pet.Owner into gj - select new - { - OwnerName = person.FirstName, - Pets = gj - }; - - foreach (var v in query) - { - // Output the owner's name. - Console.WriteLine($"{v.OwnerName}:"); - - // Output each of the owner's pet's names. - foreach (var pet in v.Pets) - { - Console.WriteLine($" {pet.Name}"); - } - } - - /* Output: - Magnus: - Daisy - Terry: - Barley - Boots - Blue Moon - Charlotte: - Whiskers - Arlene: - */ - // - } - - public static void GroupJoinXml() - { - // - // using System.Xml.Linq; - - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - - List people = [magnus, terry, charlotte, arlene]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", terry), - new("Daisy", magnus), - ]; - - XElement ownersAndPets = new("PetOwners", - from person in people - join pet in pets on person equals pet.Owner into gj - select new XElement("Person", - new XAttribute("FirstName", person.FirstName), - new XAttribute("LastName", person.LastName), - from subpet in gj - select new XElement("Pet", subpet.Name) - ) - ); - - Console.WriteLine(ownersAndPets); - - /* Output: - - - Daisy - - - Barley - Boots - Blue Moon - - - Whiskers - - - - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs deleted file mode 100644 index 98376bc8ced73..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/GroupQueryResults.cs +++ /dev/null @@ -1,240 +0,0 @@ -using static LinqSamples.Student; - -namespace LinqSamples; - -public static class GroupQueryResults -{ - /// Group by a single property in an object - public static void GroupQueryResults1() - { - // - // Variable groupByLastNamesQuery is an IEnumerable>. - var groupByLastNamesQuery = - from student in students - group student by student.LastName into newGroup - orderby newGroup.Key - select newGroup; - - foreach (var nameGroup in groupByLastNamesQuery) - { - Console.WriteLine($"Key: {nameGroup.Key}"); - foreach (var student in nameGroup) - { - Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); - } - } - - /* Output: - Key: Adams - Adams, Terry - Key: Fakhouri - Fakhouri, Fadi - Key: Feng - Feng, Hanying - Key: Garcia - Garcia, Cesar - Garcia, Debra - Garcia, Hugo - Key: Mortensen - Mortensen, Sven - Key: O'Donnell - O'Donnell, Claire - Key: Omelchenko - Omelchenko, Svetlana - Key: Tucker - Tucker, Lance - Tucker, Michael - Key: Zabokritski - Zabokritski, Eugene - */ - // - } - - /// Group by something other than a property of the object - public static void GroupQueryResults2() - { - // - var groupByFirstLetterQuery = - from student in students - group student by student.LastName[0]; - - foreach (var studentGroup in groupByFirstLetterQuery) - { - Console.WriteLine($"Key: {studentGroup.Key}"); - foreach (var student in studentGroup) - { - Console.WriteLine($"\t{student.LastName}, {student.FirstName}"); - } - } - - /* Output: - Key: A - Adams, Terry - Key: F - Fakhouri, Fadi - Feng, Hanying - Key: G - Garcia, Cesar - Garcia, Debra - Garcia, Hugo - Key: M - Mortensen, Sven - Key: O - O'Donnell, Claire - Omelchenko, Svetlana - Key: T - Tucker, Lance - Tucker, Michael - Key: Z - Zabokritski, Eugene - */ - // - } - - /// Group by numeric range and project into a new anonymous type - public static void GroupQueryResults3() - { - // - int GetPercentile(Student s) - { - double avg = s.ExamScores.Average(); - return avg > 0 ? (int)avg / 10 : 0; - } - - var groupByPercentileQuery = - from student in students - let percentile = GetPercentile(student) - group new - { - student.FirstName, - student.LastName - } by percentile into percentGroup - orderby percentGroup.Key - select percentGroup; - - foreach (var studentGroup in groupByPercentileQuery) - { - Console.WriteLine($"Key: {studentGroup.Key * 10}"); - foreach (var item in studentGroup) - { - Console.WriteLine($"\t{item.LastName}, {item.FirstName}"); - } - } - - /* Output: - Key: 60 - Garcia, Debra - Key: 70 - O'Donnell, Claire - Key: 80 - Adams, Terry - Feng, Hanying - Garcia, Cesar - Garcia, Hugo - Mortensen, Sven - Omelchenko, Svetlana - Tucker, Lance - Zabokritski, Eugene - Key: 90 - Fakhouri, Fadi - Tucker, Michael - */ - // - } - - /// - /// Group by a Boolean into two groups with string keys - /// "True" and "False" and project into a new anonymous type - /// - public static void GroupQueryResults4() - { - // - var groupByHighAverageQuery = - from student in students - group new - { - student.FirstName, - student.LastName - } by student.ExamScores.Average() > 75 into studentGroup - select studentGroup; - - foreach (var studentGroup in groupByHighAverageQuery) - { - Console.WriteLine($"Key: {studentGroup.Key}"); - foreach (var student in studentGroup) - { - Console.WriteLine($"\t{student.FirstName} {student.LastName}"); - } - } - - /* Output: - Key: True - Terry Adams - Fadi Fakhouri - Hanying Feng - Cesar Garcia - Hugo Garcia - Sven Mortensen - Svetlana Omelchenko - Lance Tucker - Michael Tucker - Eugene Zabokritski - Key: False - Debra Garcia - Claire O'Donnell - */ - // - } - - /// Group and order by a compound key - public static void GroupQueryResults5() - { - // - var groupByCompoundKey = - from student in students - group student by new - { - FirstLetterOfLastName = student.LastName[0], - IsScoreOver85 = student.ExamScores[0] > 85 - } into studentGroup - orderby studentGroup.Key.FirstLetterOfLastName - select studentGroup; - - foreach (var scoreGroup in groupByCompoundKey) - { - var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85"; - Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}"); - foreach (var item in scoreGroup) - { - Console.WriteLine($"\t{item.FirstName} {item.LastName}"); - } - } - - /* Output: - Name starts with A who scored more than 85 - Terry Adams - Name starts with F who scored more than 85 - Fadi Fakhouri - Hanying Feng - Name starts with G who scored more than 85 - Cesar Garcia - Hugo Garcia - Name starts with G who scored less than 85 - Debra Garcia - Name starts with M who scored more than 85 - Sven Mortensen - Name starts with O who scored less than 85 - Claire O'Donnell - Name starts with O who scored more than 85 - Svetlana Omelchenko - Name starts with T who scored less than 85 - Lance Tucker - Name starts with T who scored more than 85 - Michael Tucker - Name starts with Z who scored more than 85 - Eugene Zabokritski - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs deleted file mode 100644 index d5148f0494274..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/InnerJoins.cs +++ /dev/null @@ -1,410 +0,0 @@ -namespace LinqSamples; - -public static class InnerJoins -{ - public static string Basic() - { - // - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - Person rui = new("Rui", "Raposo"); - - List people = [magnus, terry, charlotte, arlene, rui]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", rui), - new("Daisy", magnus), - ]; - - // Create a collection of person-pet pairs. Each element in the collection - // is an anonymous type containing both the person's name and their pet's name. - var query = - from person in people - join pet in pets on person equals pet.Owner - select new - { - OwnerName = person.FirstName, - PetName = pet.Name - }; - - var result = ""; - foreach (var ownerAndPet in query) - { - result += $"\"{ownerAndPet.PetName}\" is owned by {ownerAndPet.OwnerName}\r\n"; - } - Console.Write(result); - return result; - - /* Output: - "Daisy" is owned by Magnus - "Barley" is owned by Terry - "Boots" is owned by Terry - "Whiskers" is owned by Charlotte - "Blue Moon" is owned by Rui - */ - // - } - - public static string BasicMethodSyntax() - { - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - Person rui = new("Rui", "Raposo"); - - List people = [magnus, terry, charlotte, arlene, rui]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", rui), - new("Daisy", magnus), - ]; - - // - var query = - people.Join(pets, - person => person, - pet => pet.Owner, - (person, pet) => - new { OwnerName = person.FirstName, PetName = pet.Name }); - // - - var result = ""; - foreach (var ownerAndPet in query) - { - result += $"\"{ownerAndPet.PetName}\" is owned by {ownerAndPet.OwnerName}\r\n"; - } - Console.Write(result); - return result; - } - - public static string CompositeKey() - { - // - List employees = - [ - new(FirstName: "Terry", LastName: "Adams", EmployeeID: 522459), - new("Charlotte", "Weiss", 204467), - new("Magnus", "Hedland", 866200), - new("Vernette", "Price", 437139) - ]; - - List students = - [ - new(FirstName: "Vernette", LastName: "Price", StudentID: 9562), - new("Terry", "Earls", 9870), - new("Terry", "Adams", 9913) - ]; - - // Join the two data sources based on a composite key consisting of first and last name, - // to determine which employees are also students. - var query = - from employee in employees - join student in students on new - { - employee.FirstName, - employee.LastName - } equals new - { - student.FirstName, - student.LastName - } - select employee.FirstName + " " + employee.LastName; - - var result = "The following people are both employees and students:\r\n"; - foreach (var name in query) - { - result += $"{name}\r\n"; - } - Console.Write(result); - return result; - - /* Output: - The following people are both employees and students: - Terry Adams - Vernette Price - */ - // - } - - public static string CompositeKeyMethodSyntax() - { - List employees = - [ - new(FirstName: "Terry", LastName: "Adams", EmployeeID: 522459), - new("Charlotte", "Weiss", 204467), - new("Magnus", "Hedland", 866200), - new("Vernette", "Price", 437139) - ]; - - List students = - [ - new(FirstName: "Vernette", LastName: "Price", StudentID: 9562), - new("Terry", "Earls", 9870), - new("Terry", "Adams", 9913) - ]; - - // - var query = employees.Join( - students, - employee => new { FirstName = employee.FirstName, LastName = employee.LastName }, - student => new { FirstName = student.FirstName, student.LastName }, - (employee, student) => $"{employee.FirstName} {employee.LastName}" - ); - // - - var result = "The following people are both employees and students:\r\n"; - foreach (var name in query) - { - result += $"{name}\r\n"; - } - Console.Write(result); - return result; - } - - public static string MultipleJoin() - { - // - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - Person rui = new("Rui", "Raposo"); - Person phyllis = new("Phyllis", "Harris"); - - List people = [magnus, terry, charlotte, arlene, rui, phyllis]; - - List cats = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", rui), - new("Daisy", magnus), - ]; - - List dogs = - [ - new(Name: "Four Wheel Drive", Owner: phyllis), - new("Duke", magnus), - new("Denim", terry), - new("Wiley", charlotte), - new("Snoopy", rui), - new("Snickers", arlene), - ]; - - // The first join matches Person and Cat.Owner from the list of people and - // cats, based on a common Person. The second join matches dogs whose names start - // with the same letter as the cats that have the same owner. - var query = - from person in people - join cat in cats on person equals cat.Owner - join dog in dogs on new - { - Owner = person, - Letter = cat.Name[..1] - } equals new - { - dog.Owner, - Letter = dog.Name[..1] - } - select new - { - CatName = cat.Name, - DogName = dog.Name - }; - - var result = ""; - foreach (var obj in query) - { - result += $"The cat \"{obj.CatName}\" shares a house, and the first letter of their name, with \"{obj.DogName}\".\r\n"; - } - Console.Write(result); - return result; - - /* Output: - The cat "Daisy" shares a house, and the first letter of their name, with "Duke". - The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley". - */ - // - } - - public static string MultipleJoinMethodSyntax() - { - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - Person rui = new("Rui", "Raposo"); - Person phyllis = new("Phyllis", "Harris"); - - List people = [magnus, terry, charlotte, arlene, rui, phyllis]; - - List cats = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", rui), - new("Daisy", magnus), - ]; - - List dogs = - [ - new(Name: "Four Wheel Drive", Owner: phyllis), - new("Duke", magnus), - new("Denim", terry), - new("Wiley", charlotte), - new("Snoopy", rui), - new("Snickers", arlene), - ]; - - // - var query = people.Join(cats, - person => person, - cat => cat.Owner, - (person, cat) => new { person, cat }) - .Join(dogs, - commonOwner => new { Owner = commonOwner.person, Letter = commonOwner.cat.Name[..1] }, - dog => new { dog.Owner, Letter = dog.Name[..1] }, - (commonOwner, dog) => new { CatName = commonOwner.cat.Name, DogName = dog.Name }); - // - - var result = ""; - foreach (var obj in query) - { - result += $"The cat \"{obj.CatName}\" shares a house, and the first letter of their name, with \"{obj.DogName}\".\r\n"; - } - Console.Write(result); - return result; - } - - public static string InnerGroupJoin() - { - // - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - - List people = [magnus, terry, charlotte, arlene]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", terry), - new("Daisy", magnus), - ]; - - var query1 = - from person in people - join pet in pets on person equals pet.Owner into gj - from subpet in gj - select new - { - OwnerName = person.FirstName, - PetName = subpet.Name - }; - - var result = "Inner join using GroupJoin():\r\n"; - foreach (var v in query1) - { - result += $"{v.OwnerName} - {v.PetName}\r\n"; - } - - var query2 = - from person in people - join pet in pets on person equals pet.Owner - select new - { - OwnerName = person.FirstName, - PetName = pet.Name - }; - - result += "\r\nThe equivalent operation using Join():\r\n"; - foreach (var v in query2) - { - result += $"{v.OwnerName} - {v.PetName}\r\n"; - } - Console.Write(result); - return result; - - /* Output: - Inner join using GroupJoin(): - Magnus - Daisy - Terry - Barley - Terry - Boots - Terry - Blue Moon - Charlotte - Whiskers - - The equivalent operation using Join(): - Magnus - Daisy - Terry - Barley - Terry - Boots - Terry - Blue Moon - Charlotte - Whiskers - */ - // - } - - public static string InnerGroupJoinMethodSyntax() - { - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - - List people = [magnus, terry, charlotte, arlene]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", terry), - new("Daisy", magnus), - ]; - - // - var query1 = people.GroupJoin(pets, - person => person, - pet => pet.Owner, - (person, gj) => new { person, gj }) - .SelectMany(pet => pet.gj, - (groupJoinPet, subpet) => new { OwnerName = groupJoinPet.person.FirstName, PetName = subpet.Name }); - // - - var result = "Inner join using GroupJoin():\r\n"; - foreach (var v in query1) - { - result += $"{v.OwnerName} - {v.PetName}\r\n"; - } - - // - var query2 = people.Join(pets, - person => person, - pet => pet.Owner, - (person, pet) => new { OwnerName = person.FirstName, PetName = pet.Name }); - // - - result += "\r\nThe equivalent operation using Join():\r\n"; - foreach (var v in query2) - { - result += $"{v.OwnerName} - {v.PetName}\r\n"; - } - Console.Write(result); - return result; - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/LeftOuterJoin.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/LeftOuterJoin.cs deleted file mode 100644 index 66587e0a42a32..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/LeftOuterJoin.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace LinqSamples; - -public static class LeftOuterJoin -{ - public static void LeftOuterJoin1() - { - // - Person magnus = new(FirstName: "Magnus", LastName: "Hedlund"); - Person terry = new("Terry", "Adams"); - Person charlotte = new("Charlotte", "Weiss"); - Person arlene = new("Arlene", "Huff"); - - List people = [magnus, terry, charlotte, arlene]; - - List pets = - [ - new(Name: "Barley", Owner: terry), - new("Boots", terry), - new("Whiskers", charlotte), - new("Blue Moon", terry), - new("Daisy", magnus), - ]; - - var query = - from person in people - join pet in pets on person equals pet.Owner into gj - from subpet in gj.DefaultIfEmpty() - select new - { - person.FirstName, - PetName = subpet?.Name ?? string.Empty - }; - - foreach (var v in query) - { - Console.WriteLine($"{v.FirstName + ":",-15}{v.PetName}"); - } - - /* Output: - Magnus: Daisy - Terry: Barley - Terry: Boots - Terry: Blue Moon - Charlotte: Whiskers - Arlene: - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/NestedGroups.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/NestedGroups.cs deleted file mode 100644 index 6cc12670f4f9b..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/NestedGroups.cs +++ /dev/null @@ -1,64 +0,0 @@ -using static LinqSamples.Student; - -namespace LinqSamples; - -public static class NestedGroups -{ - public static void NestedGroup1() - { - // - var nestedGroupsQuery = - from student in students - group student by student.Year into newGroup1 - from newGroup2 in - from student in newGroup1 - group student by student.LastName - - group newGroup2 by newGroup1.Key; - - foreach (var outerGroup in nestedGroupsQuery) - { - Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}"); - foreach (var innerGroup in outerGroup) - { - Console.WriteLine($"\tNames that begin with: {innerGroup.Key}"); - foreach (var innerGroupElement in innerGroup) - { - Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}"); - } - } - } - - /* Output: - DataClass.Student Level = SecondYear - Names that begin with: Adams - Adams Terry - Names that begin with: Garcia - Garcia Hugo - Names that begin with: Omelchenko - Omelchenko Svetlana - DataClass.Student Level = ThirdYear - Names that begin with: Fakhouri - Fakhouri Fadi - Names that begin with: Garcia - Garcia Debra - Names that begin with: Tucker - Tucker Lance - DataClass.Student Level = FirstYear - Names that begin with: Feng - Feng Hanying - Names that begin with: Mortensen - Mortensen Sven - Names that begin with: Tucker - Tucker Michael - DataClass.Student Level = FourthYear - Names that begin with: Garcia - Garcia Cesar - Names that begin with: O'Donnell - O'Donnell Claire - Names that begin with: Zabokritski - Zabokritski Eugene - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/NullValues.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/NullValues.cs index 229e112f85da4..aaf4fe4733bb6 100644 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/NullValues.cs +++ b/samples/snippets/csharp/concepts/linq/LinqSamples/NullValues.cs @@ -1,6 +1,7 @@ -using LinqSamples.Joins; +namespace LinqSamples; -namespace LinqSamples; +record Product(string Name, int CategoryID); +record Category(string Name, int ID); public static class NullValues { diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/OrderResultsOfJoin.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/OrderResultsOfJoin.cs deleted file mode 100644 index 6935a73c29fac..0000000000000 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/OrderResultsOfJoin.cs +++ /dev/null @@ -1,70 +0,0 @@ -using LinqSamples.Joins; - -namespace LinqSamples; - -public static class OrderResultsOfJoin -{ - public static void OrderResultsOfJoin1() - { - // - List categories = - [ - new(Name: "Beverages", ID: 001), - new("Condiments", 002), - new("Vegetables", 003), - new("Grains", 004), - new("Fruit", 005) - ]; - - List products = - [ - new(Name: "Cola", CategoryID: 001), - new("Tea", 001), - new("Mustard", 002), - new("Pickles", 002), - new("Carrots", 003), - new("Bok Choy", 003), - new("Peaches", 005), - new("Melons", 005), - ]; - - var groupJoinQuery2 = - from category in categories - join prod in products on category.ID equals prod.CategoryID into prodGroup - orderby category.Name - select new - { - Category = category.Name, - Products = - from prod2 in prodGroup - orderby prod2.Name - select prod2 - }; - - foreach (var productGroup in groupJoinQuery2) - { - Console.WriteLine(productGroup.Category); - foreach (var prodItem in productGroup.Products) - { - Console.WriteLine($" {prodItem.Name,-10} {prodItem.CategoryID}"); - } - } - - /* Output: - Beverages - Cola 1 - Tea 1 - Condiments - Mustard 2 - Pickles 2 - Fruit - Melons 5 - Peaches 5 - Grains - Vegetables - Bok Choy 3 - Carrots 3 - */ - // - } -} diff --git a/samples/snippets/csharp/concepts/linq/LinqSamples/Program.cs b/samples/snippets/csharp/concepts/linq/LinqSamples/Program.cs index 58fd80f1194e5..9ac780f194e40 100644 --- a/samples/snippets/csharp/concepts/linq/LinqSamples/Program.cs +++ b/samples/snippets/csharp/concepts/linq/LinqSamples/Program.cs @@ -1,5 +1,4 @@ using LinqSamples; -using LS = LinqSamples; QueryCollectionOfObjects.QueryCollectionOfObjects1(); @@ -7,41 +6,9 @@ StoreResultsOfQueryInMemory.StoreResultsOfQueryInMemory1(); -GroupQueryResults.GroupQueryResults1(); -GroupQueryResults.GroupQueryResults2(); -GroupQueryResults.GroupQueryResults3(); -GroupQueryResults.GroupQueryResults4(); -GroupQueryResults.GroupQueryResults5(); - -NestedGroups.NestedGroup1(); - -SubqueryOnGroup.SubqueryOnGroup1(); -SubqueryOnGroup.SubqueryOnGroup2(); - -GroupByContiguousKeys.GroupByContiguousKeys1(); - RuntimeFiltering.RuntimeFiltering1(); RuntimeFiltering.RuntimeFiltering2(); -InnerJoins.Basic(); -InnerJoins.BasicMethodSyntax(); -InnerJoins.CompositeKey(); -InnerJoins.CompositeKeyMethodSyntax(); -InnerJoins.MultipleJoin(); -InnerJoins.MultipleJoinMethodSyntax(); -InnerJoins.InnerGroupJoin(); -InnerJoins.InnerGroupJoinMethodSyntax(); - -GroupJoins.GroupJoin1(); -GroupJoins.GroupJoinXml(); - -LeftOuterJoin.LeftOuterJoin1(); - -OrderResultsOfJoin.OrderResultsOfJoin1(); - -CustomJoins.CustomJoins1(); -CustomJoins.MergeCsvFiles(); - NullValues.NullValues1(); Exceptions.Exceptions1();