Loading...
gadjov avatar gadjov 115 Точки

Събиране на 2 масива с LINQ

Здравейте, колеги в лекцията за LINQ е показано как се събират два масива ето с този код:

int[] a = new[] { 1, 2, 3 };
int[] b = new[] { 10, 20, 30 };

var c = a.Select ((x, index) => x + b[index]).ToArray();

Това го научих на изуст как се пише, но не го разбирам, а това е по-важно за мен. Доколкото разбирам 'х' и "индекс" са произволни, но да са лесно разбираеми. Селектираме един от 2та масива, в отделни скоби инициализираме селетнатия масив и индексацията на елемента в масива. И тук вече ми е малко тъмно , какво се случва. Х като елемент от масива А ли се явява? И защо на него не му е зададен индекс[]?

Съжалявам за тъпите въпроси и благодаря предварително!

1
Programming Basics
RoYaL avatar RoYaL Trainer 6849 Точки
Best Answer

Здравей,

 

До скоро .NET платформата беше черна кутия и как се случват нещата отдолу вероятно не е било приоритет на програмистите. Често и в СофтУни преподаваме нещата, за да успеете да ги използвате практически, но не винаги се засяга какво се случва. И съм изключително радостен когато има такива въпроси.

За щастие .NET платформата вече не е затворена. В това число и LINQ библиотеката и можем да видим нейния код на corefx/System.Linq

Ако погледнем кода на Select метода ще видим, че в най-базовия случай той има 2 overload-a. Един, който приема  Func<TSource, TResult> selector и един, който приема  Func<TSource, int, TResult> selector. Което общо взето означава, че функцията може да се използва по два начина. Единият е като й се подаде функция, която има един аргумент от типа на колекцията (TSource) и която връща произволен тип (какъвто ние решим - TResult). Вторият начин е като се подаде функция, която има два аргумента - единият е от такъв тип, какъвто е всеки елемент в колекцията, вторият е цялочислено - индексът на този елемент и връща произволен резултат, избран от нас - TResult.

Всъщност, това което се случва е:

        private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
        {
            int index = -1;
            foreach (TSource element in source)
            {
                checked
                {
                    index++;
                }

                yield return selector(element, index);
            }
        }

Минава се през всеки един елемент и за него се извиква функцията, която си подал, като й се подават на нея точно тези два аргумента - елементът и индексът. От там нататък какво ще прави функцията с тези два аргумента зависи от теб.

Това, което ти си казал е: Вземи "element" (който е един елемент от колекцията) и го събери с другМасив[index]. И както разбрахме това се случва във foreach, така че става за всеки един елемент и затова магията работи - всеки елемент се събира с елемент от друг масив на същата позиция.

В това подход естествено има проблеми, които спомена колегата enevlogiev. Ако другия масив е с по-малко елементи, например 4, а твоят с 5 и пробваш последния да го събереш с 5тата позиция на втория масив ще изгърми, заради несъществуващ 5ти елемент във втория масив.

Можеш да погледнеш и кодът на функцията, която ти е предложил enevlogiev - Zip

        private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
        {
            using (IEnumerator<TFirst> e1 = first.GetEnumerator())
            using (IEnumerator<TSecond> e2 = second.GetEnumerator())
            {
                while (e1.MoveNext() && e2.MoveNext())
                {
                    yield return resultSelector(e1.Current, e2.Current);
                }
            }
        }

Общо взето когато едната от двете колекции приключи - няма повече елементи, т.е. MoveNext върне FALSE, то ще се излезе от while цикъла и няма да се проверват повече елементи в нито една от двете колекции.

 

Поздрави,

Иван

12
16/04/2016 22:17:03
MinchoDzhagalov avatar MinchoDzhagalov 33 Точки

Select заявката връща всеки елемент от масива "a" в променливата "x" и съответния му индекс в променливата "index", а в тялото на select заявката текущият елемент от масива "а" се събира със съответстващия му по индекс от масива "b". Това най-вероятно работи само, когато масивите са с еднаква дължина или вторият е по-голям?

3
enevlogiev avatar enevlogiev 1168 Точки

Принципно си има метод за тая работа. Рядко се използва, ама все пак ..

var c = a.Zip(b, (x, y) => x + y).ToArray();

Има едно предимство, ако двете колекции имат различни дължини, ще вземе по-малката от тях и няма да има разни OutOfRange ексепшъни. Резултатът също ще е с дължина, равна на по-малката; т.е. остатъчните елементи от по-голямата колекции са игнорирани напълно.

2
16/04/2016 19:05:52
gadjov avatar gadjov 115 Точки

Благодаря ви, колеги много подробно обяснение, вече ми е ясно как работи отвътре.

0
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.