Javascript — странный. Не верите? Ну попробуйте тогда преобразовать массив строк в целые числа с помощью map и parseInt. Запустите консоль (F12 на Chrome), вставьте код ниже и нажмите Enter


['1', '7', '11'].map(parseInt);

Вместо ожидаемого массива целых чисел [1, 7, 11] мы получаем [1, NaN, 3]. Но как так? Чтобы узнать в чём тут дело, сначала нам придётся поговорить о некоторых базовых концепциях Javascript. Если вам нужен TL;DR, пролистывайте статью до самого конца.


Правдивость и ложность


Вот простой оператор if-else в Javascript:


if (true) {
    // всегда выполняется
} else {
    // не выполняется никогда
}

В этом случае условие оператора всегда истинно, поэтому блок if всегда выполняется, а блок else всегда игнорируется. Это тривиальный пример, потому что true — булев тип. Что тогда если мы поставим не булево условие?


if ("hello world") {
    // выполнится это?
    console.log("Условие истинно");
} else {
    // или это?
    console.log("Условие ложно");
}

Попробуйте запустить этот код в консоли разработчика. Вы должны увидеть «Условие истинно», так как строка «hello world» воспринимается как true.


Каждый объект в Javascript воспринимается либо как true, либо как false. При размещении в логическом контексте, таком как оператор if-else, объекты рассматриваются как true или false на основе их «истинности». Какие же объекты истинны, а какие ложны? Действует простое правило:


Все значения являются истинными, за исключением: false, 0, "" (пустая строка), null, undefined, и NaN.


Контр интуитивно это означает, что строка «false», строка «0», пустой объект {} и пустой массив [] — правдивы. Вы можете убедиться в этом самостоятельно, передав функции Boolean любой из объектов выше (например, Boolean(«0»);).


Но для наших целей просто достаточно помнить, что 0 это ложь.


Основание системы счисления


0 1 2 3 4 5 6 7 8 9 10

Когда мы считаем от нуля до девяти, мы используем разные символы для каждого из чисел (0-9). Однако, как только мы достигаем десяти, нам нужны два разных символа (1 и 0) для представления числа. Это связано с тем, что мы используем десятичную систему счисления.


Основание — наименьшее число, которое не представить только одним символом. У различных систем счисления разные основания, и поэтому, одни и те же цифры могут обозначать разные числа.


DECIMAL   BINARY    HEXADECIMAL
RADIX=10  RADIX=2   RADIX=16
0         0         0
1         1         1
2         10        2
3         11        3
4         100       4
5         101       5
6         110       6
7         111       7
8         1000      8
9         1001      9
10        1010      A
11        1011      B
12        1100      C
13        1101      D
14        1110      E
15        1111      F
16        10000     10
17        10001     11

Например, цифры 11 обозначают разные числа в этих трёх системах счисления. Для двоичной — это число 3. Для шестнадцатеричной — это число 17.


Внимательный читатель вероятно заметил что код с parseInt возвращает 3, когда вход равен 11, что соответствует двоичному столбцу из таблицы выше.


Аргументы функции


Функции в Javascript можно вызывать с любым числом аргументов, даже если их количество в сигнатуре отлично. Отсутствующие параметры рассматриваются как неопределенные, а дополнительные просто игнорируются (но хранятся в похожем на массив объекте arguments object).


function foo(x, y) {
    console.log(x);
    console.log(y);
}

foo(1, 2);       // выводит 1, 2
foo(1);           // выводит 1, undefined
foo(1, 2, 3);   // выводит 1, 2

map()


Мы почти у цели!


Map — это метод в прототипе массива, который возвращает новый массив из результатов вызова функции для каждого элемента исходного массива. Например, следующий код умножает каждый элемент массива на 3:


function multiplyBy3(x) {
    return x * 3;
}

const result = [1, 2, 3, 4, 5].map(multiplyBy3);

console.log(result);   // выводит [3, 6, 9, 12, 15];

Теперь предположим, что я хочу вывести каждый элемент используя map() (и не используя return). Можно просто передать console.log в качестве аргумента в map() … правильно?


[1, 2, 3, 4, 5].map(console.log);


Происходит что-то странное. Вместо того чтобы выводить только значение, каждый вызов console.log выводит индекс и массив полностью.


[1, 2, 3, 4, 5].map(console.log);

// эквивалентно:
[1, 2, 3, 4, 5].map(
    (val, index, array) => console.log(val, index, array)
);

// и НЕ эквивалентно:
[1, 2, 3, 4, 5].map(
    val => console.log(val)
);

При передаче функции в map() на каждой итерации она будет получать три аргумента: currentValue, currentIndex и полный array. Вот почему при каждой итерации выводятся три записи.


Теперь у нас есть всё что нужно для раскрытия тайны.


Всё вместе


ParseInt принимает два аргумента: string и radix (основание). Если переданный radix является ложным, то по умолчанию устанавливается в 10.


parseInt('11');                  => 11
parseInt('11', 2);              => 3
parseInt('11', 16);            => 17

parseInt('11', undefined);  => 11 (radix ложен)
parseInt('11', 0);              => 11 (radix ложен)

Давайте рассмотрим этот пример шаг за шагом.


['1', '7', '11'].map(parseInt);       => [1, NaN, 3]

// Первая итерация: val = '1', index = 0, array = ['1', '7', '11']

parseInt('1', 0, ['1', '7', '11']);   => 1

Так как 0 является ложным, то для основания устанавливается значение по умолчанию — 10. parseInt() принимает только два аргумента, поэтому третий аргумент ['1', '7', '11'] игнорируется. Строка '1' по основанию 10 даст результат 1.


// Вторая итерация: val = '7', index = 1, array = ['1', '7', '11']

parseInt('7', 1, ['1', '7', '11']);   => NaN

В системе по основанию 1 символа '7' не существует. Как и в случае с первой итерацией, последний аргумент игнорируется. Таким образом parseInt() возвращает NaN.


// Третья итерация: val = '11', index = 2, array = ['1', '7', '11']

parseInt('11', 2, ['1', '7', '11']);   => 3

В двоичной системе счисления '11' относится к числу 3. Последний аргумент вновь игнорируется.


Итог (TL;DR)


['1', '7', '11'].map(parseInt) не работает как было задумано, потому что map передает три аргумента в parseInt() на каждой итерации. Второй аргумент index передается в parseInt в качестве параметра radix (основание системы счисления). Таким образом, каждая строка в массиве анализируется с использованием недефолтного основания. '7' анализируется по основанию 1, что даёт NaN; '11' анализируется как двоичное число — итог 3. '1' анализируется по дефолтному основанию 10, потому что его индекс 0 является ложным.


А вот код, который будет работать так, как мы хотели:


['1', '7', '11'].map(numStr => parseInt(numStr));


Комментарии (134)


  1. alexesDev
    17.06.2019 10:38
    -1

    ['1', '7', '11'].map(numStr => parseInt(numStr));

    На практике этот код не работает как мы хотели до ECMAScript5. Работает такой


    ['1', '07', '11'].map(numStr => parseInt(numStr, 10));


    1. constb
      17.06.2019 11:20
      +2

      в «до ECMAScript5» у вас и стрелочная функция будет вызывать Syntax Error. :)


      1. alexesDev
        17.06.2019 11:25
        -2

        Не будет, если это старая нода и babel.


        1. Myateznik
          17.06.2019 11:38
          +2

          Из-за «babel» это уже не про ECMAScript5. В ES5 по спецификации нет стрелочных функций. А кейс «старая нода и babel» это уже синтаксис ES6+ с трансляцией в синтаксис ES5 посредством babel.


          1. alexesDev
            17.06.2019 12:40

            Без понятия что за придирки. То, что я написал — почти стандартное eslint правило — всегда писать radix. Еще есть старые браузеры. И большая часть сайтов которые написаны на "es6" это "синтаксис ES6+ с трансляцией в синтаксис ES5 посредством babel".


    1. ruSl0n
      18.06.2019 11:06

      Я не понимаю, а чего заминусили то? Автор сам попал в ошибку, очевидно — надо смотреть определение функции map, она в коллбэк (как и многие другие функции массивов) отдает элемент, индекс и массив, и использовать как в заголовке статьи по умолчанию не правильно.

      У автора проблема из пальца высосана, и основана на внимательности или не знании базовой библиотеки.

      На счет стрелочных функций, если вы используете parseInt (а это если по простому — deprecated и надо использовать Number.parseInt) то используйте обычное определение функции
      Соотвественно

      ['1', '7', '11'].map(function(val, idx, array){ return parseInt(val)});


    1. YemSalat
      19.06.2019 11:41

      В большинстве случаев можно использовать:

      ['1', '2', '3'].map(Number)


  1. Get-Web
    17.06.2019 11:01

    Хорошая подача и интересный материал. Спасибо


  1. yarkov
    17.06.2019 11:15

    В 2011 году уже писали, но напомнить будет не лишним. Спасибо.


  1. JustDont
    17.06.2019 11:32
    +11

    Вот оно, тонкое искусство написать несколько килобайт текста с картинками вместо «parseInt принимает два аргумента».


    1. w0den
      17.06.2019 14:34

      Автор ещё не знал, что map() передаёт несколько аргументов. Таким образом, всю эту Санта-Барбару можно сократить до: RTFM.


  1. i360u
    17.06.2019 11:55
    +3

    Ага, JavaScript настолько странный, что не зная какие аргументы принимает функция можно выстрелить себе в ногу. Кто бы мог подумать. Статья занимательная конечно, но обычно при встрече с любой подобной «странностью» вопрос решается за минуту на MDN.


    1. cubit
      17.06.2019 12:17

      То что люди не понимают(не знают), то для них странное)


    1. devpony
      17.06.2019 16:15
      +1

      Просто все нормальные языки реализуют map :: [a] -> (a -> b) -> [b], а JS зачем-то решил отличиться. Поэтому и странный.


      1. YemSalat
        19.06.2019 18:31

        Наверное чтобы не добавлять еще одну переменную для текущего индекса?
        И можно было писать в одну строчку:
        ['а', 'b', 'c'].map((el, index) => `${el} at index ${index}`)

        Пример из жизни когда это полезно (react)

        render () {
         return items.map((el, index) => <li element={el} tabindex={index} />)
        }


        1. khim
          20.06.2019 00:58

          А вам серьёзно ради этого нужно Map'у портить?

          render () {
           var index = 0;
           return items.map((el) => `<li element={el} tabindex={index} />`)
          }
          Это прям так ужасно? Катастрофа? Много вы таким способом строк наэкономили? А если много — то почему web-странички занимают мегабайты?


          1. YemSalat
            20.06.2019 08:07
            +1

            Не ужасно и не катастрофа, но добавляет проблем, например имя `index` может быть уже занято в этом скоупе, надо будет новое придумывать. Или например, у вас несколько .map'ов — будете делать var index1..indexN? С таким же успехом можно сказать что вместо map написать for — в принципе тоже не катастрофа.


            1. khim
              20.06.2019 17:58
              -1

              Вместо той mapы, что в JavaScript уж точно лучше написать for — хотя бы иллюзий не будет.


        1. devpony
          20.06.2019 03:26

          Если необходим индекс, его всегда можно изящно добавить в одну строчку, просто построив список пар с помощью функции zip (не силён в JS, не знаю местных аналогов).


          map (\(element, index) -> ...) $ zip elements [0..]

          Из простых атомарных частей (map, zip, генераторы) очень легко строить абстракции любой сложности, на какие только воображения хватит. Зачем все возможные варианты пытаться уместить в одной сложной функции – совершенно не понятно.


          1. YemSalat
            20.06.2019 08:12
            +2

            Зачем все возможные варианты пытаться уместить в одной сложной функции – совершенно не понятно.

            Никто не пытается уместить там ВСЕ возможные варианты, просто добавили еще один аргумент, которые в веб программировании довольно часто используется.
            В JS .map — это метод списка, который уже загружен в память. Зачем использовать ненужные абстракции, где одна функция покрывает 99% нужд?


          1. YemSalat
            21.06.2019 03:13

            devpony khim Cerberuser ребят, до меня только сейчас дошло что вы похоже не в курсе, что в js обычно функции, у которых несколько аргументов, никто просто по имени в .map не передает. Обычно их оборачивают в анонимную функцию, где уже можно передать те аргументы, которые нужны. В большинстве случаев передается как-раз только первый аргумент.
            Т.е. никто на самом деле не делает вот так:

            list.map(parseInt)

            Обычно делают так:
            list.map(x => parseInt(x, 10))

            Какой разумный человек вообще будет использовать parseInt без второго аргумента, учитывая его не совсем очевидное поведение
            Поэтому не люблю подобные статьи в стиле «как выстрелить себе в ногу в js» — они вводят людей в заблуждение. У всех языков есть свои особенности, которые могут казаться не очевидными на первый взгляд, но при работе с языком обретающими смысл.


    1. vyatsek
      17.06.2019 17:03

      Просто надо читать спеку и все, а не полагаться на свою интерпретацию и тогда ничего странного не будет.


      1. khim
        17.06.2019 18:29

        А когда вам два плюс два сложить хочется — вы тоже спеку читаете? Map'а — она штука фундаментальная, про неё аж целая статья есть в Википедии. То есть для человека, знакомого с функциональщиной — это как операция сложения или как синус. То есть место, где меньше всего ожидаешь подвоха и уж точно не полезешь в спеку про него читать.

        А так-то да: многие знания — многия печали, если ничего не знать и воспринимать JavaScript «с чистого листа» — то всё нормально.


        1. w0den
          17.06.2019 22:33
          -2

          А когда вам два плюс два сложить хочется — вы тоже спеку читаете?
          Вы не поверьте ©

          Например, имеем выражения '2'+2 и '2'*2. Как думаете, не читая документацию и не имея опыт работы с C#, Java, JavaScript, PHP и Python, Вы сможете сказать, каков будет результат этих выражений для каждого языка? Думаю, ответ очевиден: при таких условиях, ни Вы, ни кто-либо другой не сможет правильно ответить на этот вопрос. И в этом нет ничего удивительно, поскольку у каждого языка есть свои тараканы «единственные правильные способы» решить тут или иную задачу.


          1. Bas1l
            18.06.2019 01:29

            Вызов map в этой аналогии все-таки эквивалентен именно 2+2, и для такого случая спеку читать большинство людей не будет (и правильно сделает—для большинства языков, кроме javascript).


            1. w0den
              18.06.2019 12:51

              Читайте эти выражения как getVar() + getInput() и getVar() * getInput(), потом спросите себя, что вызывает больше путаницы для начинающего программиста, который не читает документацию: «неверное» поведение map или «неверный» результат этих выражений?

              Я не знаю, откуда у Вас такие данные, что «для такого случая спеку читать большинство людей не будет», но я более чем уверен, что хорошие программисты всё-таки читают документацию. И это правильно.


          1. khim
            18.06.2019 02:22

            Например, имеем выражения '2'+2 и '2'*2. Думаю, ответ очевиден: при таких условиях, ни Вы, ни кто-либо другой не сможет правильно ответить на этот вопрос.
            На самом деле ответ очевиден: языки, в которых эти выражения вообще имеют какой-то смысл — языки опасные и ими пользоваться не стоит. И да, я знаю, что в python второе выражение имеет смысл — это одно из неудачных мест в этом языке.


            1. w0den
              18.06.2019 09:17

              Ого! То есть, C#, C++, Go, Java, JavaScript, PHP и другие, это опасные языки и не стоит ими пользоваться? Возможно, только Python правильнее всех, но и у него тоже есть свои «тараканы»?

              Получается, нам нужно срочно создать новый идеальный язык?
              image


              1. khim
                18.06.2019 16:33

                Нет, получается, что распространённые языки неидеальны, а идеальные — нераспространены.

                Что довольно логично: если вы постоянно выпрявляете «косяки» в языках, то ими, рано или поздно, престают пользоваться (пример: Algol-W, Pacal, Modula-2, Oberon), а если не правите… ну так они и остаются.

                Однако из-за того, что все распространённые языки обладают своими граблями — они гряблями быть не перестают. Но PHP и JavaScript особенные: — там плотность граблей такова, что кажется, что язык только из одних граблей и состоит. Хуже только bash… но это, всё-таки не «универсальный язык программирования» и никогда так не позиционировался.


                1. w0den
                  18.06.2019 19:04

                  Что касается идеальных языках, Вы не задумались, почему они не стали популярными? Быть может, не такие они уж идеальны? Например, (не считая BASIC, который был лишь детской забавой), мой первый язык программирования был Pascal и, опираясь на мои давние воспоминания, лично я не могу называть его идеальным ЯП. Возможно я ошибаюсь, но мне кажется ни один язык не сможет стать идеальным для всех задач и для всех людей.

                  Для меня грабли в языках программирования скорее философский вопрос: я пишу на разных языках и почти не вижу никаких проблем с граблями — каждый ЯП это лишь очередной инструмент со своими особенностями. И кстати, возможно, мои задачи не являются комплексными, но мне кажется, в JavaScript исправили одно из фундаментальных граблей: простой способ получить индекс при использовании map. Конечно, это не означает, что я сразу же перейду на JavaScript и буду использовать его вместо Python или PHP.

                  Кстати, как насчёт «опасных языках» и чтение документации? Вы действительно думаете, что не стоит использовать те языки и что не нужно читать документацию? Спрашиваю, не холивара ради, а потому что ниже Вы пишите правильные вещи (ну, с моей точки зрения), и мне действительно интересен честный ответ на эти вопросы.


                  1. khim
                    18.06.2019 23:25

                    Что касается идеальных языках, Вы не задумались, почему они не стали популярными?
                    Потому что «всюду лошади» (вот эти вот лошади). Невозможно что-то сделать правильно и хорошо с первого раза. Более того, то, что было хорошо 10 лет назад сегодня — может оказаться уже и не таким удачным решением. Но когда вы что-то исправляете — вы возращаетесь, на шкале популярности, назад. Ибо «лошади».

                    мой первый язык программирования был Pascal и, опираясь на мои давние воспоминания, лично я не могу называть его идеальным ЯП.
                    Я тоже. Потому что в нём есть проблема «провисающего» else, хотя бы. Зато уже в нём оператор выбора реализован «по-людски». То есть, объективно говоря, Ada или Modula-2 — лучше, безопаснее, чем Pascal, а C или Java — хуже и опаснее. Про JavaScript и PHP я вообще молчу — это не языки, а коллекция граблей.

                    Однако стоит признать, при этому, что попытки сделать «идеальный» язык (Algol W, Pascal, Modula-2, Ada и Oberon и так далее) привели к тому, что хотя, объективно говоря, каждый следующий язык был лучше предыдущего… в популярности они при этом теряли. До такой степени, что последние версии Oberon — это уже не промышленные языки, а, скорее, «исторический курёз».

                    А выгрывали языки, похожие на популярные: C, C++, Java, JavaScript. Они все унаследовали от C проблему с идиотским оператором выбора (сколько ошибок произошло от «потерянного» break это ж ни в сказке сказать, ни пером описать… на один случай, когда fallthrough сделан умышленно приходится в среднем, наверное десяток, а то и сотня случаев, когда это сделано по ошибке) и также имеют общую для С и Pascal проблему с else. Из языков в десятке самых популярных только два языка не имеют этой проблемы: Python и, внезапно, Visual Basic .NET.

                    Но означает ли это, что все эти популярные языки — уродские (да) и их следует избегать (нет)? Нет, потому что качество самого языка — это ещё не всё. Вы можете взять какой-нибудь распрекрасный Oberon… и утонуть в попытка сделать что-либо подобное на Eigenу. В одиночку. С нуля. Вряд ли это будет разумным выбором.

                    Потому приходится выбирать: брать язык, который не слишком ужасен — и при этом достаточно популярен для того, чтобы вам не пришлось всё «оснастку» писать с нуля.

                    И кстати, возможно, мои задачи не являются комплексными, но мне кажется, в JavaScript исправили одно из фундаментальных граблей: простой способ получить индекс при использовании map.
                    Пока вы ещё не привели пример ни одной задачи, при которой вам это было бы нужно. И вопрос: а почему мы передаём в функцию три аргумента, а не, скажем, десять?

                    Решение, которое использует python мне кажется куда более оптимальным — если вам так уж нужен индекс вариант map(function, enumerate(object)) — всегда к вашим услугам. Незачем для этого ломать простую семантику метафункции map.

                    Вы действительно думаете, что не стоит использовать те языки и что не нужно читать документацию?
                    Конечно же документацию читать нужно. Но это не значит, что если в языке что-то сделано нелогично и неудобно, но это всё подробно описано в документации — то это, вдруг, делает язык приемлемым. Потому что каждое место, где что-то сделано нелогично и неудобно — обозначает, что об это место вы будете, потом, больно стукаться. Регулярно. Если не вы, то кто-то из работающих с вами. Как об этот самый дурацкий switch.

                    И потому, когда вы читаете документацию — всегда полезно соотносить количество «граблей», которые есть в языке и описаны в документации с количеством полезных фич.

                    В этом смысле лидером, мне кажется, является Python: даже несмотря на динамическую типизацию (которую я не люблю) очень многие вещи там сделаны качественно, интуитивно понятно и не требуют чтения кучи документации, чтобы разобраться в простом фрагменте.

                    А вот, скажем, C++ — он хорош в другом: там очень много «острых» углов, но если вам нужна максимальная эффективность — то в нём есть много инструментов, которых нет в других языках… возможно сюда, со временем, доберётся Rust… но пока там нет, в стабильной версии, даже вызова ассемблерных вставок — то есть о максимальной эффективности речь [пока?] не идёт. Но за ним я внимательно слежу, это реально может быть замена C++ со временем.

                    мне действительно интересен честный ответ на эти вопросы.
                    Честный ответ таков: все популярные языки — неидеальны, но эта неидеальность — разная. Если JavaScript и PHP я буду использовать, разве что, от безисходности — когда у меня нет другого выбора, то вот python или C++ — я вполне могу использовать того, когда выбор есть.

                    Потому что python — вполне пригодно использовать там, где требования к скорости не критичны. То есть, в первую очередь, во вспомогательных системах, с которым конечный пользователь не сталкивается, но зато сталкиваюсь я: в этом случае для меня не важно, сколько времени потребует программа для работы — важно суммарно время её работы и время её написания.

                    Писать же неэффективную программу, за которую вам деньги платят, как тут предлагают… ну это халтура просто.

                    Бытовая аналогия: я, например, если ну очень приспичит, могу разобрать свой лаптоп с помощью вилки и перечинного ножа. Но если я увижу что такое попытается, при мне, проделать ремонтник — я отберу у него лаптоп и откажусь платить деньги. Потому что профессионал — должен обладать профессиональным инструментом и уметь им пользоваться.

                    Хотя если какой-то самодур заставит профессионалов ремонтировать лаптопы без каких-либо железных предметов (безопасность, всё такое) — я, конечно, посочувствую, похихикаю, но приму их оправдания и пойму почему они все теперь — виртуозы по разборке этих лаптопов зубочистками.

                    Но вне этого странного места я, всё-таки, буду ожидать использование отвёртки.

                    То же самое и с программированием: если менеджер порождает скрипт для закачки файлов в VBA в Excel (реальный пример с одним из моих друзей)… да ради бога — чем умеет, тем и порождает.

                    Если фронтэндер использует JavaScript… ну тут вопросов тоже нету: а что ещё он может использовать?

                    Или если вам нужен скрипт для установки программ: sh или даже bash — они везде есть, а вот pyhton, увы, через раз — то есть Python 2, то есть Python 3, под каким именем — это тоже неизвестно…

                    Но вот когда этот же JavaScript на бекэнд тянут… где можно выбрать из кучи других, более вменяемых, языков… тут уже возникают вопросы по поводу профессионализма… в полный рост возникают.


                    1. w0den
                      19.06.2019 16:38

                      Спасибо большое за столь развёрнутый ответ. Не понимаю, почему Вам поставили минус.

                      В принципе, со многими мыслями согласен, особенно с тем, что нужно выбрать правильные инструменты и всегда нужно стремиться писать хороший код, скорость и удобочитаемость которого должны быть на первом месте.

                      Тоже не понимаю эту тягу к использованию JavaScript на бэкенде и для создания десктопных приложений. Ну, я понимаю, когда разработчик, зная лишь JavaScript, делает свой маленький проект, но мне трудно согласиться с теми, кто переходят на JavaScript, несмотря на то, что из-за этого проект будет работать в разы медленнее и будет рождать куча новых интересных приключений.

                      Тем не менее, я думаю, что плохой код, прежде всего, рождается по вине разработчика. Возможно, это не очень правильная позиция, но лично я не пытаюсь искать слабые места каждого языка. Наоборот, я рассматриваю его сильные стороны в определённый момент для конкретной задачи.

                      Поскольку моя работа связана с вебом, для меня главные показатели при выборе языка это быстродействие + время разработчики + стоимость поддержки инстанса. Если есть возможность, использую ab и siege, чтобы понять, что должен выбрать. А если нет, просто выбираю технологии, полагаясь на свой интуицию опыт.

                      Пока вы ещё не привели пример ни одной задачи
                      Извините, но не заметил такой вопрос. Например, в JavaScript имеем возможность написать this.getAll().map(this.markupRows) а метод markupRows сможет:
                      — сравнивать текущий элемент с предыдущем/следующим элементом
                      — узнать еслу текущий элемент это последний/первый элемент
                      — обработать первые n элементов особым образом
                      — добавить классы для стилизации таблицы-зебры

                      а почему мы передаём в функцию три аргумента, а не, скажем, десять?
                      К сожалению, не могу ответить, поскольку не изучал этот вопрос, но полагаю потому что эти три аргументы более чем достаточно, чтобы принимающая функция смогла обрабатывать текущий элемент в рамках всего списка.

                      если вам так уж нужен индекс вариант map(function, enumerate(object))
                      Возможно я ошибаюсь, но думаю что оптимальнее всё-таки использовать map(function, object), при этом map должен передать функции все необходимые аргументы, а уже в function будем решать, что делать с ними (в том числе, игнорировать ненужные аргументы).

                      если менеджер порождает скрипт для закачки файлов в VBA в Excel
                      Признаюсь, у меня тоже есть парочку xlsm и docm файлов в которых использую VBA, чтобы при определённых событьях отправить или загрузить данные прямо из Excel или Word. Более того, у меня есть много мелких «помошников» с использованием Autoit, Bash, HTA, Perl, Powershell, Windows Batch. Как правило, они рождались довольно спонтанно и без долгих раздумий (или из желания узнать что-то новое), но для меня важен лишь тот факт, что на протяжения многих лет они делали всю кропотливую работу.


                      1. khim
                        20.06.2019 01:30

                        Возможно, это не очень правильная позиция, но лично я не пытаюсь искать слабые места каждого языка.
                        Как можно осмысленно выбирать языки если не знать и сильных и слабых сторон?

                        Вы никогда не задумывались почему JavaScript и PHP — это «ужас летящий на крыльях ночи», почему C — это коллекция граблей, за которые вас всё время стремится наказать компилятор и так далее?

                        Это ведь не случайность. JavaScript и PHP — изначально постулировали задачу «сделать так, чтобы разработчик без опыта мог создать скрипт — он бы начал работать с как можно меньшими усилиями». Какое для этого было выбрано средство? Язык старается «додумывать за программиста — и не всегда успешно. Когда я пишу „333“/3 — я, вообще, чего получить-то хочу? 2222 (111 делённое 3) или „3“ (треть от исходной строки)? Идеи о том, какой тут будет ответ у разных людей могут сильно разниться.

                        Эта же Mapа с дополнительным аргументом в сочетании с функциями, которые имеют неочевидные дополнительные аргументы… это же всё — из попытки „помочь“.

                        Только в больших проектах эта „помощь“ боком выходит.

                        Ну а C — это, напротив, попытка „развязать руки компилятору“ за счёт ограничения программиста. Что, в результате, приводит язык к „близости к железу“ (ибо компилятору не нужно лишних проверок в код вставлять, чтобы a << 257 правильно обработать), а это, в свою очередь — к попыткам использовать C как „переносимый ассемблер“.

                        Ну и так далее.

                        А вот эта проблема с else — это уже чистой воды случайность. Можно понять почему так получилось, но глубокой идеи за этим не стоит… особенно в Pascal, где, скажем, record всегда имеет свой end… а вот if — не имеет. Просто недодумали вовремя.

                        Извините, но не заметил такой вопрос. Например, в JavaScript имеем возможность написать this.getAll().map(this.markupRows) а метод markupRows сможет:
                        — сравнивать текущий элемент с предыдущем/следующим элементом
                        — узнать еслу текущий элемент это последний/первый элемент
                        — обработать первые n элементов особым образом
                        — добавить классы для стилизации таблицы-зебры
                        Хорошие примеры. Хотя вопросы безопасности тут сразу встают в полный рост. Потому что представьте себе, что вы добавили „классы для стилизации таблицы“ — а потом, в рекламируемом тут .chained подходе выкинули часть элемнтов? И что будет с вашими „первыми n элементами“, если их число, дальшейними фильтрами, изменится?

                        Все эти вещи очень-очень сильно выходят за рамки простой концепции Mapы, которая предполагает обработку множества элементов одинаковым образом. И может, в некоторых языках, даже автоматом раскидать эту обработку по разным ядрам процессора — в JavaScript это в принципе невозможно… именно из-за выбранного интерфейса.

                        Возможно я ошибаюсь, но думаю что оптимальнее всё-таки использовать map(function, object), при этом map должен передать функции все необходимые аргументы, а уже в function будем решать, что делать с ними (в том числе, игнорировать ненужные аргументы).
                        Ну разумеется нет. Если вы передаёте в function этот самый object — то вы должны гарантировать, во-первых, что этот object во-первых существует, а во-вторых никто к нему, кроме этой функции Map доступа не имеет. Это сериализует всё операцию и очень сильно связывает руки реализации. Dart, кстати, во многом отсюда: разработчики V8 обнаружили, что достигли „потолка“, когда вот эменно подобные „эффективные“ решения не позволяют сделать UI быстрее… и решили всех пересадить на новый язык.

                        Никакого успеха, понятно, не достигли — но это уже другая история.


                        1. YemSalat
                          20.06.2019 13:50

                          И может, в некоторых языках, даже автоматом раскидать эту обработку по разным ядрам процессора — в JavaScript это в принципе невозможно… именно из-за выбранного интерфейса.

                          И в чем принципиальная проблема раскидать обработку map, даже с дополнительными аргументами, по нескольким процессорам? (то что Array не immutable сейчас нe учитываем)


                          1. khim
                            20.06.2019 19:21

                            А что вам мешает ездить на JavaScript-велосипеде по дороге? Отсутствие колёс сейчас не учитываем.


                            1. YemSalat
                              20.06.2019 23:22
                              -1

                              Правильно, ляпнуть какую-нибудь глупость, а когда спросят — отмочить очередную прибаутку про то какой js корявый.
                              Вы же сами написали: " в JavaScript это в принципе невозможно… именно из-за выбранного интерфейса"
                              Ну и при чем тут не иммутабельный список? Он и без дополнительных аргументов в .map такой.


                              1. khim
                                21.06.2019 01:11

                                Ну и при чем тут не иммутабельный список? Он и без дополнительных аргументов в .map такой.
                                И ровно поэтому mmap в JavaScript — не Mapа.

                                У математической функции Map есть в сущности ровно одно несколько фундаментальных свойство: (map f)?(map g) = map (f ?g). Оно не выполняется в JavaScript ровно потому, что Mapа там — нет Mapа.

                                Причём этому мешают оба «улучшения», которые имеются в JavaScript: мы можем как изменить объект для которого вызвана Map, но, кроме того, во время обработки — мы обязаны передавать туда объект — который там, в функции, может поменяться.

                                Если необходиомость передавать индекс — это просто вопрос производительности (для многих структур само вычисление индекса весьма нетривиально, особенно если обработка ведётся на многих ядрах и/или машинах параллельно), то вопрос метабельной коллекции, передаваемой в функцию — это уже более принципиальный вопрос.

                                P.S. Обратите, кстати, внимание, не мой пример с push и подумайте — сколько «стоит» поддержание всего этого. В том-то и дело, что «улучшенный» интерфейс, принятый в JavaScript — он далеко не бесплатен. Это если даже забыть про «проблемы идиотов-математиков» (а как бы классно без них было… никаких проблем с Mapой, JavaScriptом и компьютерами… в силу отсутствия и первого и второго и третьего)


                                1. YemSalat
                                  21.06.2019 01:41

                                  У математической функции Map есть в сущности ровно одно несколько фундаментальных свойство: (map f)?(map g) = map (f ?g). Оно не выполняется в JavaScript ровно потому, что Mapа там — нет Mapа.

                                  list.map(x => fun1(x)).map(х => fun2(x)) -> list.map(x => fun2(fun1(x)))

                                  Где тут оно не выполняется?

                                  Да, js позволяет передавать функции по имени, и можно вместо list.map(x => fun(x)) написать list.map(fun) — и вполне логично, что тогда он передаст туда все свои аргументы.

                                  (для многих структур само вычисление индекса весьма нетривиально

                                  В js список — вполне определенная структура.

                                  вопрос метабельной коллекции, передаваемой в функцию — это уже более принципиальный вопрос.

                                  А ничего что у вас и так есть ссылка на этот список?
                                  `list.map(x => list.push(1)) // ?!`
                                  Вы же говорили что именно из-за аргументов передаваемых в map в js его в принципе невозможно распараллелить, что неверно.


                        1. w0den
                          20.06.2019 13:57

                          Как можно осмысленно выбирать языки если не знать и сильных и слабых сторон?
                          Прошу отметить, что вырвать фразы из контекста не очень хорошо, ведь я написал другое. Цитирую: «лично я не пытаюсь искать слабые места каждого языка. Наоборот, я рассматриваю его сильные стороны в определённый момент для конкретной задачи». Например, если на php-fpm+nginx я смогу создать некое веб-приложение, которое будет работать на VPS за $2.99, обработать сотни тысячи запросов в день и держать load average ниже 0.1, при этом мне не придётся кувыркаться из-за простых вещей вроде strip_tags, то я не перестану использовать PHP только из-за идеологических соображений. Однако это не означает, что всегда буду использовать PHP. Также как, я не буду рекомендовать кому-либо изучить PHP в качестве первого ЯП (конечно, бывают исключения).

                          Вы никогда не задумывались почему JavaScript и PHP — это «ужас летящий на крыльях ночи», почему C — это коллекция граблей, за которые вас всё время стремится наказать компилятор и так далее?
                          Как я уже сказал, для меня каждый язык программирования лишь очередной инструмент и я указал, как выбираю свои инструменты. В таких случаях, лично я, далеко не в первую очередь буду рассуждать о плохом синтаксисе, идеологии или грабли ЯП.

                          Потому что представьте себе, что вы добавили „классы для стилизации таблицы“ — а потом, в рекламируемом тут .chained подходе выкинули часть элемнтов? И что будет с вашими „первыми n элементами“, если их число, дальшейними фильтрами, изменится?
                          Во-первых, мой пример уже содержит тот самый .chained, иначе без него я бы написал let users = this.getAll(); users.map(this.markupRows); (т.е. определить ненужную переменную и написать больше кода). Во-вторых, как уже сказал, я считаю, что плохой код рождается по вине разработчика и если разработчик сначала будет сделать всю тяжёлую работу, а потом выкинуть часть обработанных элементов — ни один язык его не спасёт. Для меня выглядит «немного неправильно» получить все записи сортированные в определённом порядке, применить HTML разметку для каждой записи, а потом выкинуть часть элементов.

                          Все эти вещи очень-очень сильно выходят за рамки простой концепции Mapы, которая предполагает обработку множества элементов одинаковым образом.
                          Получается, даже разработчики Python нарушают эту концепцию? Например, беглый поиск находит это (уверен, это не единственный случай). Но, главное, мне интересно, где написано, что нужно обрабатывать элементы одинаковым образом? Например, Википедиа говорит, что map применяет некую функцию к каждому элементу списка и возвращает обработанные элементы в том же порядке. Лично я не видел какие-либо ограничения, на что должна возвращать функция, также как и какие аргументы, она должна получать. И разве, это не те «самые лошади», когда другие не могут усовершенствовать функцию из-за боязни/нежелании сломать «старые концепции»?

                          Ну разумеется нет. Если вы передаёте в function этот самый object — то вы должны гарантировать, во-первых, что этот object во-первых существует
                          Если честно, не понял, что Вы имеете в виду, и как передача дополнительных аргументов влияет на это.

                          а во-вторых никто к нему, кроме этой функции Map доступа не имеет.
                          Хм. Почему никто не должен иметь доступ к этому объекту?


                          1. khim
                            20.06.2019 20:36

                            Для меня выглядит «немного неправильно» получить все записи сортированные в определённом порядке, применить HTML разметку для каждой записи, а потом выкинуть часть элементов.
                            А почему, собственно? Вы когда-нибудь Excel'ем или Google Docs пользовались? Там фильтры есть. Можно выкинуть все строчки со словом «красный» или с числами больше 100 или ещё какие-нибудь. И таблички с кнопочками которые реагируют на эти действия. Вы что — считаете, что это нормально, когда на каждый чих табличка формируется с нуля?

                            Во-вторых, как уже сказал, я считаю, что плохой код рождается по вине разработчика и если разработчик сначала будет сделать всю тяжёлую работу, а потом выкинуть часть обработанных элементов — ни один язык его не спасёт.
                            Плохой код рождается от непонимания того, что и где у вас происходит в программе, в первую очередь. А для такого понимания — нужно, чтобы программа состояла из простых компонент. «Обработать 100 элементов по одному и вернуть их» — это простая концепция. Mapа в JavaScript — сложная. Ну вот просто сложная. Вот вы можете сказать что сделает вот такой простой код:
                            [1, 2, 3].map((x, i, a) => a.push(i))
                            Ну там, по наитию, не читаю мануалы до посинения? А почему?

                            Я где-то читал классную фразу «Good language have to be not just easy to use, but it should be hard to abuse». При этом вторая часть — на самом деле важнее первой.

                            Получается, даже разработчики Python нарушают эту концепцию? Например, беглый поиск находит это (уверен, это не единственный случай).
                            Я не знаю что именно вы искали и почему то, что вы нашли нарушает концепцию Mapы, извините.

                            Лично я не видел какие-либо ограничения, на что должна возвращать функция, также как и какие аргументы, она должна получать.
                            А очень просто — в той же википедии, если бы прочитали статью до конца, вы бы увидели там фундаментальное свойство Mapы:
                            (map f)?(map g) = map (f ?g).

                            Это такое же фундаментальное свойство Mapы, как коммутативность у сложения или умножения. Это, собственно, то, что делает Mapу Mapой и над которой потом строятся другие вещи (типа MapReduce).

                            В JavaScript Mapа — она, я извиняюсь, не Mapа. Так зачем использовать неподобающее имя?

                            И разве, это не те «самые лошади», когда другие не могут усовершенствовать функцию из-за боязни/нежелании сломать «старые концепции»?
                            Отчасти да. Накидайте-ка ссылок на научные журналы с обсуждением функций высшего порядка, совместимых с примером выше? Нету? А почему нету?

                            А я отвечу почему: потому что нельзя создать теорию «кучи мусора».

                            Ну разумеется нет. Если вы передаёте в function этот самый object — то вы должны гарантировать, во-первых, что этот object во-первых существует
                            Если честно, не понял, что Вы имеете в виду, и как передача дополнительных аргументов влияет на это.
                            Очень просто. Возьмите тот же самый MapReduce. Когда мы обрабатываем петабайты данных. Что мы должны передать в Mapу в качестве третьего аргумента и зачем? Или вот такой код
                            def HelloReader():
                               with open('hello.txt') as hello:
                                 for line in hello:
                                   yield line
                            
                            map(lambda(x) : "| " + x, HelloReader())
                            Тут, вы, конечно, можете передать в Mapу генератор — но какой в этом смысл и что вы там с ним будете делать? А что будет происходить с вашей Mapой если функция начнёт из контейнера объекты удалять?

                            Например, если на php-fpm+nginx я смогу создать некое веб-приложение, которое будет работать на VPS за $2.99, обработать сотни тысячи запросов в день и держать load average ниже 0.1, при этом мне не придётся кувыркаться из-за простых вещей вроде strip_tags, то я не перестану использовать PHP только из-за идеологических соображений.
                            А зачем мне может потребоваться такое приложение? Если это мой проект — то я, уж как-нибудь найду $5, чтобы не вмазываться в это гуано, а если это на заказ, то я просто не буду с ним связываться. Потому что если у закачика нету $50-60 в год на оплату хостинга, то откуда у него возьмутся какие-то деньги, чтобы заплатить за работу мне?

                            Времена, когда форум на PHP можно было захостить за $15-20 в месяц, а личная VDS с гигабайтом памяти начиналась от $200-$500 — в далёком-далёком прошлом. Сегодня за $5 в месяц можно получить гиг памяти и 25GB на SSD — чего хватает даже для каких-нибудь Enterprise Java решений под небольшую нагрузку. Зачем, в этих условиях, связываться с PHP — я представить себе не могу.


        1. vyatsek
          17.06.2019 22:34

          А когда вам два плюс два сложить хочется — вы тоже спеку читаете?
          Но в первый раз вообще читаю, да. Нет привычки фантазировать за авторов, а потом еще и жаловаться на свою же лень прочитать спеку. Количество миносв к предыдущему посту, говорит лишь о качестве программистов, которые не копают чуть глубже.

          javascript язык с динамическими типами, по этому надо всегда понимать какая сигнатура метода будет вызывана и с какими аргументами, более того приколов с преобразованием типов полно. Очевидно что в языке динамическая типизация добавляет сложности и это потенциально слабое место, а значит ему надо уделить больше внимания.
          А синус тоже может принимать и радианы и градусы и грады, прежде, чем воспользоваться уточню единицы.


          1. khim
            18.06.2019 02:23

            А синус тоже может принимать и радианы и градусы и грады, прежде, чем воспользоваться уточню единицы.
            Ну и в каком языке он принимает грады по умолчанию, извините?


            1. Cerberuser
              18.06.2019 05:07

              Импортозамещение и не до такого доведёт...


            1. vyatsek
              18.06.2019 13:23

              Ну и в каком языке он принимает грады по умолчанию, извините?
              Не знаю, не интересовался. То, что мейнстримовые языки используют радианы, далеко не повод рассуждать так обо всех языках.


            1. Ndochp
              18.06.2019 17:33

              МК-61, и не по умолчанию, а по положению переключателя на клавиатуре.


          1. scumware
            18.06.2019 11:45

            >javascript язык с динамическими типами, по этому надо всегда понимать какая сигнатура метода будет вызывана и с какими аргументами…
            ужс!
            Я уже настолько привык, что такие вещи мне помогает контролировать компилятор, что сейчас для меня это уже дико.
            У нас туда даже пройдя ревью, и цепкие лапы QA к клиентам баги попадают, а если ещё и вот такое стрелять будет… Нет, спасибо: в жизни и так достаточно сложностей, а моё внимание и память не безграничны. И вообще, в отличие от языков курильщика, компилятор здорового человека не позволяет себе ногу отстрелить, по крайней мере не таким примитивным способом.
            Я вот об этом:

                    static void Main(string[] args)
                    {
                        //WARNING - CS0642 : Возможно ошибочный пустой оператор
                        for (long i = 0; i < int.MaxValue; i++);{}
            
                        double a, b, c; //WARNING -CS0168
                        a = b = 1; //WARNING -  CS0219
            
                       var dsf = new int[] {1, 2, 3};
                       //ERROR CS1661: Невозможно преобразовать "лямбда-выражение" к типу делегата "System.Func<int,int,double>",
                       //поскольку типы параметров не совпадают с типами параметров делегата
            
                       //ERROR CS1661: Невозможно преобразовать "лямбда-выражение" к типу делегата "System.Func<int,int,double>",
                       //поскольку типы параметров не совпадают с типами параметров делегата
                      Enumerable.Select<int, double>( dsf, ( double p1, double p2) => { return p1 * p2;});
                    }
            


            1. khim
              18.06.2019 16:40

              JavaScript — это не «язык курильщика», а «язык менеджера».

              Там, где вы закроете одну таску, человек работающий на JavaScript, закроет десять. И неважно, что у него на это уйдёт вдвое больше времени, и что девять из этих десяти будут следствиями «закрытой» первой.

              На графике у менеджмента у вас будет сиротливо висеть одна закрытая за пару дней таска, а у JavaScript-разработчика две, а то и три в день.

              Так кто лучше работает?


              1. scumware
                18.06.2019 17:38

                Десять тасков, где 9 — следствие криво закрытой первой, — это неверный подход. Правильный подход — переоткрывать таску до тех пор, либо пока фича не будет работать, либо пока нерадивого «менеджера» не уволят.
                У нас переоткрывают. Логика простая: высокому начальству не интересно сколько тасок закрыто. Интересно какие фичи реализованы, и что пойдёт в текущий релиз (например, что будет в release notes).

                Количество тасок — это очковтирательство.


                1. khim
                  18.06.2019 18:05
                  +1

                  У нас переоткрывают.
                  А как это делается, извините? Ну вот представьте себе: вы закрыли таску «добавить иконку в профиль пользователя». А через неделю открыли другую «в сводной таблице нет информации и количестве покупателей, купивших товар». Как вы вообще поймёте, что эти таски связаны? И что покупатели пропали потому что в поле «Фамилия» у вас теперь не строка, а сложный объект со ссылкой на иконку?

                  Да, вы проведёте расследование, возможно, укажите на то, что эти таски связаны… но это уже потом — в процессе решения проблемы!

                  Как «переоткрыть таску» увидев таблицу с пустой колонкой — ума не приложу.


                  1. scumware
                    18.06.2019 20:58

                    >А как это делается, извините? Ну вот представьте себе: вы закрыли таску «добавить иконку в профиль пользователя». А через неделю открыли другую «в сводной таблице нет информации и количестве покупателей, купивших товар».

                    Не, не так. У нас взмах крыла бабочки к взрыву сверхновой не приводит. Такие таски чаще всего переоткрываются с «у части пользователей неверная иконка, или её нет», или «в логах NullReferenceException». Происходит это потому, что иконки/картинки/подсказки часто зависят от локали пользователя или от его организации. Т.е., например, в базовой версии c культурой en-us всё хорошо, а в кастомизированной с испанской локалью — нет. Или «шаг вперёд — шаг назад иконка пропадает».


                    1. khim
                      18.06.2019 21:38
                      -1

                      Не, не так. У нас взмах крыла бабочки к взрыву сверхновой не приводит.
                      Ну так то у вас. У вас её и добавить-то за час, засунув в поле, которое, так-то, строку должно содердать, маленькую структурку в одном месте, а потом «вернуть всё взад» в другом, не получится.

                      А в JavaScript — вполне. Потому и получаем «язык менеджера», что таски закрываются быстро, бурно, понять — кто именно налажал, зачастую тяжело (так что и наказывать-то обычно некого). Красота же.

                      А что результат дерьмо — ну так это ещё осознать нужно, что, при отсутствии вот совсем-совсем прямого конкурента — не так-то просто сделать…


        1. YemSalat
          19.06.2019 23:13

          Map'а — она штука фундаментальная, про неё аж целая статья есть в Википедии.

          Ну вот вам цитата из вашей ссылки на википедию:
          map — функция высшего порядка, используемая во многих языках программирования, которая применяет данную функцию к каждому элементу списка, возвращая список результатов. При рассмотрении в функциональной форме она часто называется «применить-ко-всем».

          Чем Array.map в js не соответствует этому описанию?

          То есть место, где меньше всего ожидаешь подвоха и уж точно не полезешь в спеку про него читать.
          И конечно то самое место, куда будешь пихать любую функцию, независимо от ее аргументов…


          1. khim
            20.06.2019 01:42

            Чем Array.map в js не соответствует этому описанию?
            Хотя бы тем, что вы не можете гарантировать, что вызов функции для одного элемента будет зависеть только от этого элемента. Всё из той же статьи:

            Математическая основа операции map позволяет проводить множество оптимизаций.
            (map f · map g) xs (где «·» — оператор композиции функций) эквивалентно map (f · g)
            то есть (map f)?(map g) = map (f ?g). Эта оптимизация избавляет от необходимости в двукратном вызове map за счёт совмещения применения функций f и g.
            Так вот, сюрприз-сюрприз, для Javascript — эта оптимизация невозможна. Именно из-за того, что map тут — это не map, а чёрт знает что.

            И конечно то самое место, куда будешь пихать любую функцию, независимо от ее аргументов…
            Ну, как бы, ты ожидаешь, что аргумент-то будет один, да.

            Но как я уже сказал, главная проблема Mapы в JavaScript — это не проблема на КДПВ (это впросто завлекуха), а в том, что Mapа в JavaScript — это не Mapа, == — это не операция сравнения, < и > — это не операции порядка и так далее. Подвох на каждом шагу. Хуже, чем в C.


            1. YemSalat
              20.06.2019 07:42

              Хотя бы тем, что вы не можете гарантировать, что вызов функции для одного элемента будет зависеть только от этого элемента.
              Не понял. Вот вызов для одного элемента — `['1'].map(Number)` Что тут нельзя гарантировать?

              Так вот, сюрприз-сюрприз, для Javascript — эта оптимизация невозможна. Именно из-за того, что map тут — это не map, а чёрт знает что.
              Как это невозможна?
              Вместо list.map(fun1).map(fun2) можно писать: list.map(x => fun2(fun1(x)))
              Если я неправильно понял — можете привести правильный пример на Python/Ruby?

              Mapа в JavaScript — это не Mapа, == — это не операция сравнения, < и > — это не операции порядка и так далее. Подвох на каждом шагу.
              Во-первых, не путайте Map() и .map(), все-таки разные вещи.
              И что за «операции порядка»? Они не гуглятся.
              Во-вторых, да — в JS есть исторические косяки. Люди, которые на нем пишут не первый день о них знают, это не проблема.


              1. Cerberuser
                20.06.2019 09:09

                Не понял. Вот вызов для одного элемента — ['1'].map(Number) Что тут нельзя гарантировать?

                Речь о другом. Если мы передаём в map какую-то функцию, которая принимает несколько аргументов, то результат для каждого элемента потенциально может зависеть от всего исходного массива, а не только от самого этого элемента.


                Как это невозможна?
                Вместо list.map(fun1).map(fun2) можно писать: list.map(x => fun2(fun1(x)))

                Нельзя, если fun1 и/или fun2 принимает несколько аргументов.


                И что за «операции порядка»? Они не гуглятся.

                Отношения порядка, если быть точным.


                1. YemSalat
                  20.06.2019 10:08

                  Речь о другом. Если мы передаём в map какую-то функцию, которая принимает несколько аргументов, то результат для каждого элемента потенциально может зависеть от всего исходного массива, а не только от самого этого элемента.
                  Но опять же — это не проблема .map'a в JS.
                  Я изначально отвечал на:
                  > вы не можете гарантировать, что вызов функции для одного элемента будет зависеть только от этого элемента.
                  Если функция имеет только один аргумент — то можно гарантировать. Если у нее несколько аргументов, то она и вызывается внутри map с несколькими аргументами. В реальных случаях, если нужно вызвать функцию с несколькими аргументами внутри map — то будет использоваться «обертка» из стрелочной функции.

                  Нельзя, если fun1 и/или fun2 принимает несколько аргументов.
                  Но в таком случае у вас будет:
                  list.map(x => fun1(x)).map(x => fun2(x)) == list.map(x => fun2(fun1(x)))
                  Где тут «для Javascript — эта оптимизация невозможна»?
                  [EDIT] Сори, затупил, имелся в виду вызов с несколькими аргументами.
                  Но тогда прошу привести пример подобного на другом не-функциональном языке программирования.
                  list.map(x => fun1(x, y)).map(x => fun2(x, y)) == list.map(x => fun2(fun1(x, y), y))
                  Чем это отличается?

                  Отношения порядка, если быть точным.
                  Но я все-равно не понимаю какое отношение это имеет к операторам < и > в JS.


                  1. Cerberuser
                    20.06.2019 10:55

                    Но опять же — это не проблема .map'a в JS.

                    Если я правильно понимаю логику изначального автора — именно что проблема. Преобразование, которое в математике называется map, такого поведения иметь не должно.


                    Но я все-равно не понимаю какое отношение это имеет к операторам < и > в JS.

                    В том-то и дело, что никакого. А должно иметь, потому что в математике отношения "больше" и "меньше" — это отношения порядка.


                    1. YemSalat
                      20.06.2019 11:07

                      Преобразование, которое в математике называется map, такого поведения иметь не должно.

                      Какого поведения? Надо определиться — мы говорим о математике, или о деталях синтаксиса и реализации. [1, 2, 3].map(x => fun(x)) — чем это не map из математики?

                      В том-то и дело, что никакого. А должно иметь, потому что в математике отношения «больше» и «меньше» — это отношения порядка.

                      Можете пожалуйста привести пример на каком-нибудь языке программирования (не функциональном) где эти операторы имеют значение отношения порядка.


                      1. Cerberuser
                        20.06.2019 11:15

                        Можете пожалуйста привести пример на каком-нибудь языке программирования (не функциональном) где эти операторы имеют значение отношения порядка.

                        Любой язык с сильной типизацией. Rust, Python, Java, далее везде.


                        1. YemSalat
                          20.06.2019 11:25

                          Я видимо правда не знаю, но чем отличаются операторы < >, например в Java и JS?
                          Только тем что в js можно сравнить разные типы?


                          1. Cerberuser
                            20.06.2019 11:31

                            Тем, что в JS, в отличие от Java, верна цепочка 12 <= "13" <= "5" <= 12, т.е. нарушается транзитивность.


                            1. YemSalat
                              20.06.2019 12:04

                              Но какое отношение это имеет к нашему разговору?
                              В Java вы такую цепочку вообще не напишете. В Python chained comparison поддерживается только на уровне синтаксического сахара.


                              1. Cerberuser
                                20.06.2019 12:09

                                Самое прямое. Вы спрашиваете, в каких языках оператор сравнения играет роль отношения порядка. Я отвечаю, что это верно для языков со строгой типизацией. В отличие от JS, где имеется приведённый контрпример. И да, если Вам это принципиально, я могу его переписать так: (12 < "13") && ("13" < "5") && ("5" < 12) — тогда "синтаксический сахар" будет ни при чём, как и должно быть.


                                1. YemSalat
                                  20.06.2019 12:34

                                  Вы спрашиваете, в каких языках оператор сравнения играет роль отношения порядка. Я отвечаю, что это верно для языков со строгой типизацией.
                                  А я вам еще раз повторяю, что то как работают операторы сравнения в разных языках не зависит от того, сильно типизированные они или нет. это все детали имплементации конкретных языков.

                                  Например, сильно-типизированный python (в реализации CPython2) — при сравнении разных типов — сравнивает строки названий их типов (о как!)
                                  Поменяйте местами типы в вашем выражении и python2 вам так же радостно скажет что оно — True.
                                  (12 < «13») and (13 < «5») and (5 < «12»)

                                  ПС большинство языков со строгой типизацией вам вообще не даст сравнить строку с числом.


                                  1. khim
                                    20.06.2019 20:46

                                    Поменяйте местами типы в вашем выражении и python2 вам так же радостно скажет что оно — True.
                                    (12 < «13») and (13 < «5») and (5 < «12»)
                                    И именно потому что это — дерьмо собачье в Python3 это исправили. Если бы они, при этом, ещё и работу со строками не поломали… Впрочем в Python 3.7-3.8 всё уже более-менее удобоваримо…

                                    ПС большинство языков со строгой типизацией вам вообще не даст сравнить строку с числом.
                                    Конечно — это их главное преимущество.


                                    1. YemSalat
                                      20.06.2019 23:31

                                      По-моему очевидно, что вы в теме весьма поверхностно разбираетесь, при этом почему-то считаете своей обязанностью написать про то как в js все «фигово и не математично..»
                                      А на деле — копнешь чуть глубже и вы начинаете путаться в терминах и уходить от темы.


                                      1. khim
                                        21.06.2019 01:18

                                        А на деле — копнешь чуть глубже и вы начинаете путаться в терминах и уходить от темы.
                                        В теме написания говнопрограмм я действительно не разбираюсь. Да и не хочу разбираться.

                                        Вообще показательно, что там где я перепутал термины и написал вместо «отношение порядка» «операции порядка» — вы пошли гуглить, а не указали на путаницу.

                                        Это одна из фундаментальных вещий — но, конечно, знатоках JavaScript она ни к чему.

                                        А четыре арифметических действия не хотите поправить или улучшить? А то там, может, тоже математики налажали.

                                        Или их вы тоже сейчас гуглить пойдёте?


                                        1. YemSalat
                                          21.06.2019 01:48

                                          Я учился на английском, поэтому некоторые термины на русском гуглю.
                                          Вместо того чтобы ерничать — вы бы лучше написали чем отличаются операторы <> в python от js, помимо сильной типизации в первом и слабой во втором.


                  1. Cerberuser
                    20.06.2019 10:58

                    list.map(x => fun1(x, y)).map(x => fun2(x, y)) == list.map(x => fun2(fun1(x, y), y))
                    Чем это отличается?

                    Речь, опять же, не об этом (аргумент y у вас берётся из ниоткуда). Пусть у нас функция fun2 принимает три аргумента (а fun1, для простоты, только один). Тогда:


                    list.map(fun1).map(fun2) — третий аргумент fun2 — преобразованный массив.


                    list.map((x, i, arr) => fun2(fun1(x), i, arr)) — третий аргумент fun2 — исходный массив.


                    1. YemSalat
                      20.06.2019 11:17
                      +1

                      Речь, опять же, не об этом (аргумент y у вас берётся из ниоткуда)

                      Речь всю ветку была о том, что в JS .map не как в математике. В математике в map передается по сути только один аргумент — текущий элемент. Все остальные аргументы там могут взяться только «из ниоткуда»
                      В математике нет никаких дополнительных аргументов в виде индексов и исходных массивов, они добавлены в JS для удобства. Что не делает JS'овский .map «менее математичным»


                      1. khim
                        20.06.2019 20:48

                        Что не делает JS'овский .map «менее математичным»
                        Это делает JS'овский map не имеющим никакого отношения к математике. Всё равно как если бы у вас символом "+" обозначалась операция деления, а сложения в языке вообще не было бы.

                        Вы бы были готовы работать на таком языке? Я — нет. Вот и с JavaScript такая же история: в тех местах, где его «обойти» нельзя, я, конечно, с ним общаюсь. Но стараюсь делать как можно больше на других, более вменяемых, языках.


                        1. YemSalat
                          21.06.2019 00:07

                          Я работаю на js и python, и точно не стал бы говорить что в js все так глупо и неудобно, как «если бы у вас символом „+“ обозначалась операция деления»

                          Вы бы были готовы работать на таком языке? Я — нет. Вот и с JavaScript такая же история: в тех местах, где его «обойти» нельзя, я, конечно, с ним общаюсь. Но стараюсь делать как можно больше на других, более вменяемых, языках.
                          А вам подобные топики проходить мимо чувство собственной важности не позволяет?


    1. YuryB
      17.06.2019 23:12

      да, но вы ведь не держите в голове дамп MDN, от API нужна предсказуемость, чего JS не обеспечивает. а с такой логикой как у вас можно вообще безошибочно на чём угодно писать и все языки-api простые :)


      1. taujavarob
        18.06.2019 20:35

        от API нужна предсказуемость, чего JS не обеспечивает.
        Ему этого не надо. Он не для этого создавался. Не для API предсказуемости.

        Ну, а то что стал языком для всего — ну так уж вышло.

        Это как вывернутый «наизнанку» глаз зрячего хордового животного, но — «Акцентирование внимания только на одной детали, без попытки найти ей рациональное объяснение в более широком контексте строения глаза как целого и условий его функционирования у конкретного организма, делает аргумент о «нерациональности» скорее эмоциональным, чем научным»

        Вспоминая Докинза. (С)


      1. YemSalat
        19.06.2019 18:44

        да, но вы ведь не держите в голове дамп MDN, от API нужна предсказуемость, чего JS не обеспечивает.

        Еще как обеспечивает. Все методы списков: map/filter/forEach — передают одни и те же аргументы. То что это работает не так как в языке, на котором вы писали раньше — как раз значит что вам надо было, перед тем как начинать программировать, изучить мануалы.
        Ну еще, чтобы не держать в голове весь MDN —
        можно использовать нормальные ide


        1. khim
          20.06.2019 01:46

          то что это работает не так как в языке, на котором вы писали раньше
          Проблема в том, что это не работает как в математике, откуда все эти понятия взяты. То есть весь математический аппарат, вокруг этого наработанный, можно забыть.

          Так зачем смущать людей, используя названия, которые влекут за собой вполне определённых ожидания — если вы, потом, эти ожидания нарушаете?

          Можно использовать нормальные ide<ёblockquote>А можно использовать нормальные языки, которые не требуют, как идиоту, тыкать в каждую букву на экране, чтобы понять что она значит.

          И кстати, вы не показали, что ваша IDE покажет на Map'у — что, как раз, гораздо интереснее и важнее.


          1. YemSalat
            20.06.2019 09:01
            +1

            Проблема в том, что это не работает как в математике, откуда все эти понятия взяты. То есть весь математический аппарат, вокруг этого наработанный, можно забыть.

            Так зачем смущать людей, используя названия, которые влекут за собой вполне определённых ожидания — если вы, потом, эти ожидания нарушаете?
            Большинство не-функциональных языков работают не «как в математике».
            Приведите пример на каком-нибудь Python.

            И кстати, вы не показали, что ваша IDE покажет на Map'у — что, как раз, гораздо интереснее и важнее.

            Вот что покажет


  1. Amareis
    17.06.2019 12:30
    +1

    ['10', '20', '33'].map(Number)

    Можно ещё так, слегка попроще получается.


    1. kogoruhn
      17.06.2019 13:59

      Можно, но если массив будет содержать не только строки или пустые строки, результаты будут отличаться от тех, что отдаст parseInt.


      1. YemSalat
        19.06.2019 18:55

        Фишка в том что `map(a => parseInt(a))` в большинстве случаев используют там, где вполне можно обойтись кастом в Number.
        В js полно новичков, они задают вопрос про «как конвертнуть строку в число в JS», и получают самый популярный ответ — parseInt. Поэтому лепят его где надо и не надо.
        По моему опыту, parseInt — штука весьма специфичная и реально требуется в довольно редких юзкейсах.


  1. sanchezzzhak
    17.06.2019 13:46
    +1

    Общая рекомендация от Google итд parseInt надо вызывать с redux параметром.


    1. JustDont
      17.06.2019 13:50
      +1

      Radix != redux. Just sayin'.



  1. Aingis
    17.06.2019 14:28

    На самом деле не понимаю такую страсть к parseInt, когда Number работает почти всегда лучше. Даже в CSS-значениях типа "1.5px" уже нередко можно встретить дробные значения (достаточно включить масштабирование) и parseFloat будет работать лучше.


    > ['1px', '7px', '11px'].map(parseFloat);
    < (3) [1, 7, 11]


  1. user_man
    17.06.2019 14:53
    +1

    Интересно, а какой умник придумал передачу всего на свете при использовании map?

    Функциональные языки программирования обычно передают один параметр — элемент списка (хотя сам список может строиться на основании других структур данных). В данном случае видим крайне «очевидный» подход — а зачем передавать только один параметр? А давайте передадим 3. А почему не 4? А может лучше 5? Не, вот 3 — это самое оно! Но почему? Было бы интересно узнать ответ. Да к тому же для разных структур данных. Например — для множества, где нет индексов. Или для дерева (при использовании соответствующей функции map).

    В целом вижу очередное пренебрежение строгоостью. То есть нет явной проверки типов в JS, а потому можно всё — передавать в функцию любое количество параметров, передавать параметры любого типа, вообще не передавать параметры, полная свобода! Но заканчивается такая свобода огромным количеством неявных косяков.

    Хотя да, это холиварная тема «строгая типизация против абсолютной вольности».


    1. extempl
      17.06.2019 17:54
      +2

      Да вроде JS не один такой, кто при итерации выводит как минимум два параметра — value и key. В JS при этом третий параметр это вся итерируемая коллекция, что не совсем понятно зачем (ну как непонятно, при .chained подходе может быть удобно, когда итерируемая коллекция неизвестна заранее, например, после .filter), но вроде в каждом итераторе так. В чём здесь вольность — мне непонятно. Особенность? Возможно. Но она известна, задокументирована, и в большинстве своём не вызывает проблем.


      Тут вопрос интеграции одних методов с другими (и только для случая передачи функции в качестве аргумента), что, в общем, может быть актуально для любых языков с достаточно сильным стремлением к обратной совместимости, но при этом активно развивающимися.


      1. khim
        17.06.2019 19:01

        В чём здесь вольность — мне непонятно.
        Вольность в том, что Map — это вещь фундаментальная. Никому ведь не приходит в голову сделать умножение двух элементов таким, чтобы оно, внезапно, передавало в функцию дополнительный аргумент. Хотя есть, конечно, С++ со своим operator++(int);… но там, тоже, это решение считается «не самым удачным» (это если ещё мягко сказать).


        1. mclander
          17.06.2019 23:47
          +3

          Ну так не надо передавать ссылку на сужую функцию куда не просят. Есть же стрелки и обертки.


          Индекс в map бывает очень удобен. И это фундаментально и офигенно.


        1. mclander
          17.06.2019 23:56
          +1

          Сужую = чужую, ох уж это. m.habr


        1. stepmex
          18.06.2019 17:32

          А где там сказано что в вызываемую функцию должно передаваться только текущее значение цикла? JS не нарушает фундаментального map, он дополняет его в пределах своей особенности. В JS все встроенные итераторы цикла передают 3 аргумента: значение, индекс и коллекцию. Возьмите forEach, map, filter или любую другую подобную функцию. Так что в данном случае JS делает как раз то что очевидно и привычно для него.
          Никакой отсебятины или нарушений стандартов. Всё в соответствии с документацией.


          1. khim
            18.06.2019 18:08
            -1

            А где там сказано что в вызываемую функцию должно передаваться только текущее значение цикла?
            Вы по ссылке-то ходили? Map — это функция высшего порядка, позволяющая применить другую функцию ко всем элементам списка. Ещё представить себе, что если туда передадут функцию с несколькими параметрами, то они будут «прозрачно» переданы из Map в саму вторую функцию (каррирование) можно, то вот передача каких-то «посторонных» элементов — это кошмар.


            1. stepmex
              18.06.2019 18:30
              +1

              Ходил. И там нет никаких ограничений на количество и тип параметров у вызываемой функции.
              Ваша интерпретация статьи меня не сильно интересует. Я задал конкретный вопрос на который вы не даёте ответа.
              Фундаментальность функции не нарушена, она выполняет свои функции так как описано.


      1. movl
        17.06.2019 19:03

        Да даже при фильтрации это можно очень удобно использовать.

        [1, 1, 2, 2, 2, 3, 4].filter((x, i, a) => a.indexOf(x) === i) // [1, 2, 3, 4]


        1. khim
          17.06.2019 19:11

          Жуть какая. Ваши сайты работают слишком быстро и тратят мало памяти? Спросите movl — он научит этот недостаток исправлять!

          Какой-нибудь [...new Set([1, 1, 2, 2, 2, 3, 4])] делает то же самое — но обходится без O(N?)…


          1. theWaR_13
            17.06.2019 19:41
            +1

            Ну т.е. вы хотите сказать, что вы никогда не используете второй аргумент (индекс) при использовании .map, .filter и иже с ним? Или может быть вы всегда в callback передаёте функцию так, как это показано в статье и потом на выходе получаете некорректные результаты? Ведь можно же всегда явно обозначить все аргументы, а дальше отправить их уже в нужную функцию.
            Я не буду спорить про фундаментальность Map, но как минимум первые два аргумента в JS нахожу крайне полезными.


            1. khim
              18.06.2019 02:28

              Давайте так: говорить что я вот вообще никогда-никогда его использовать не буду я не стану. Никогда не говори никогда. Но спомнить случай, когд он был бы полезен я навскидку не могу. Если можете — приведите пример.


            1. Cerberuser
              18.06.2019 05:58

              Да, разумеется, можно явно обозначить все аргументы, если нам нужно что-то, кроме самих элементов:


              vector.iter().enumerate().map(|(index, element)| mapper(element, index))

              mapped = map (\(ch, ind) -> mapper ch ind) (zip list [0..])

              А если, кроме элементов, больше ничего не нужно — то никто нам ничего лишнего подсовывать не должен.


              1. xGromMx
                18.06.2019 14:38

                mapped mapper = zipWith mapper [0..]


          1. movl
            17.06.2019 19:44
            +2

            А Вы хорош! Правда речь вроде шла про использование chaining методов, но никак не про преждевременную оптимизацию.


            1. khim
              18.06.2019 02:32
              -1

              Речь шла, извините, про преждевременную пессимизацию.

              И да, любой код, который имеет сложность, очевидно ассиптотически большую чем оптимальная — я потребую прокомментировать и объяснить почему в данном конкретном случае его можно использовать.


              1. movl
                18.06.2019 03:46

                Вы же понимаете, что асимптотическая сложность алгоритма в отрыве от набора данных не является показателем эффективности работы кода? Но в данном случае я согласен с тем, что Ваш пример обычно будет работать быстрее, по множеству причин. Вот только утверждение, что там сложность меньше чем O(n?), по-хорошему, нуждается в доказательствах. Причем если мы говорим про язык, а не про конкретные реализации, то и доказательством должно являться требование к реализации структуры Set в спецификации языка.


                И раз уж Вы прям затребовали комментариев о причинах возможного использования подобного кода, то причины могут заключаться в удобстве этого использования, о чем я заявил изначально. Chaining методы удобны тем, что их можно писать друг за другом, а значит мой пример предполагал возможность выбора уникальных значений списка, без разрыва цепочки преобразований. И именно на этом я делаю акцент еще раз.


                Выбирать, что в каждом конкретном случае выгоднее: оптимизация, или скорость разработки, или читаемость кода, или любой другой параметр, — это вопрос каждого конкретного случая. Ну только если у Вас вдруг не затерялся где-то рецепт серебряной пули. Потому считаю Ваши выпады предвзятыми и не претендующими хоть на какую-то объективность. В связи с этим откланиваюсь.


                1. khim
                  18.06.2019 17:18
                  +1

                  Вы же понимаете, что асимптотическая сложность алгоритма в отрыве от набора данных не является показателем эффективности работы кода?
                  Не является. Но на практике ситуация, когда рассчитывали на 5 элементов, а получили 50 — более, чем типична. Джефф Дин пропагандирует подход 10x: если вы считаете, что ваш код будет обрабатывать, скажем, сто элементов — то писать его нужно так, чтобы тысяч никого не напрягла. А вот если потребуется миллион — тут уже можно и переписать что-то.

                  Вот только утверждение, что там сложность меньше чем O(n?), по-хорошему, нуждается в доказательствах.
                  Нет, не нужно. Set предназначен для той операции, которую вы хотите проделать. Если он реализован не оптимально — то это, обычно, уже не ваша проблема.

                  Да, иногда проколы встречаются и в стандартной библиотеке обнаруживаются странные вещи (типа сортировки, которая можете вырождаться в O(n?)), но их, как раз, можно «разово» пофиксить, написав свою реализацию. 100500 мест, где вы сами, своими руками, создали себе O(n?) — можно пофиксить только путём переписывания.

                  И раз уж Вы прям затребовали комментариев о причинах возможного использования подобного кода, то причины могут заключаться в удобстве этого использования, о чем я заявил изначально.
                  И именно поэтому я требую не устного объяснения, а комментария в коде. Подробного. Строчек на 10. Со ссылками на документы, которые гарантируют нам, что в данном конкретном случае O(n?) нас устраивает. Если подобное описание написать проще, чем всё сделать оптимально — ну Ok, возможно, в данном конкретном случае этот варинт и подходит. Но в большинстве случаев эта экономия — копеечная, а последующие тормоза — вполне реальны.

                  Chaining методы удобны тем, что их можно писать друг за другом, а значит мой пример предполагал возможность выбора уникальных значений списка, без разрыва цепочки преобразований. И именно на этом я делаю акцент еще раз.
                  А я ещё раз сделаю акцент на том, что вы экономите несколько минут разработчика один раз, а страдать потом приходится пользователям годами.

                  Если вы пишите проекты «в корзину» с подходом «после нас хоть потоп»… ну это — ваш выбор. Я в таких развлечениях стараюсь не участвовать.

                  Выбирать, что в каждом конкретном случае выгоднее: оптимизация, или скорость разработки, или читаемость кода, или любой другой параметр, — это вопрос каждого конкретного случая.
                  Конечно. Вопрос в дефолте. Если у вас дефолт — писать «тяп-ляп», то ваши программы всегда будут тормозить. Если дефолт — писать аккуратно — не будут. И на практике написать ассмптотически оптимально — почти всегда не в 10 раз сложнее, чем «тяп-ляп»…


            1. DistortNeo
              18.06.2019 17:36

              Очередной пример, показывающий то, что если что-то завозят в JS, то только наполовину: map, reduce есть, а groupBy из коробки — нет.


          1. mclander
            17.06.2019 23:54
            +5

            Блин вот никогда мне не жалко было ресурсов при обработке массива из 7 значений. Особенно, если есть требование о поддержки дерьма мамонта от мелкософт. И лучше использовать O(n**2), чем думать как транспайлер справится с Set.


            Оптимизация и так дешевых фрагментов один из способов ни хрена полезного не делать, но чувствовать свою нужность


            1. khim
              18.06.2019 02:38
              -2

              Оптимизация и так дешевых фрагментов один из способов ни хрена полезного не делать, но чувствовать свою нужность
              Откуда вы знаете что эти фрагменты будут дешёвыми? Откуда вы знаете что там всегда будет 7 значений?

              Правило-то простое: не пишите, без должной причины, код, который имеет ассимтотически неоптимальную сложность — и вам не придётся его оптимизировать.

              И лучше использовать O(n**2), чем думать как транспайлер справится с Set.
              Зачем транспайлеру как-то особо справляться с Set, если он поддерживается во всех современных браузерах? И я лучше напишу код, который будет нормально работать на всех современных браузерах, чем буду думать о MS IE 9.

              Вообще странная идея: использовать стерлочные функции и при этом рефлексировать по поводу Map и Set.


  1. Odrin
    17.06.2019 15:59

    Но почему? Было бы интересно узнать ответ.
    Потому что это удобно.
    Функциональные языки программирования обычно передают один параметр
    JS — не функциональный ЯП.


    1. user_man
      18.06.2019 13:44

      А ещё было бы удобно передавать десяток дополнительных параметров. Но не передают ведь. Неудобно!

      Есть типизированный подход и есть брейнфак. Вот JS — это по сути брейнфак, потому что без зубрёжки толстенной спецификации — всё будет неочевидным и непонятным. Преимущество типизированных языков здесь очевидно — не надо зубрить столько УГ.


  1. polRk
    17.06.2019 16:45

    ['1', '7', '11'].map(v => +v);
    

    Но есть и отличия с parseInt
    ['1', '7', '11', null, undefined, '12s3'].map(v => parseInt(v,10));
    // [1, 7, 11, NaN, NaN, 12]
    ['1', '7', '11', null, undefined, '12s3'].map(v => +v);
    // [1, 7, 11, 0, NaN, NaN]
    


  1. r1alex
    17.06.2019 18:10

    Кто-от обязательно этот вопрос включит в собеседование. Хорошо это или плохо?


    1. merhalak
      17.06.2019 23:47
      +1

      Хорошо, потому что с JS один черт придется работать. А значит, стоит знать о подводных камнях. Обычный вопрос на ширину кругозора.


  1. xitt
    17.06.2019 18:57

    Я бы для обьяснения эффекта лаконично переписал

    ['1', '7', '11'].map((v, i, iteratee)=>parseInt(v, i));


    1. aleki
      18.06.2019 11:44
      +1

      Только вот понятнее особо не стало. Куда вы деваете время, сэкономленное на именах переменных?

      ["1", "7", "11"].map((value, index, array) => parseInt(value, index));
      


      1. xitt
        18.06.2019 18:51

        Так еще лучше, спасибо.


      1. polRk
        19.06.2019 16:13

        Только это не правильно. parseInt в качестве второго аргумента принимает основание системы счисления!


        1. aleki
          19.06.2019 17:52

          Так задача и не стояла показать как правильно. Человек показал, что происходит на самом деле.


  1. MSC6502
    17.06.2019 20:48

    Ай да JS, ай да Бренданов сын. Думаешь, что находишь сумму, а на самом деле, в зависимости от аргументов может быть как вычитание, так и умножение.
    Уж лучше бы VB развивали, было бы меньше непоняток и самоотстреленных ног.
    Да и комьюнити было бы в разы больше.


    1. bano-notit
      17.06.2019 21:36
      +4

      Я очень надеюсь, что это сейчас постирония была...


      1. khim
        18.06.2019 02:39

        Нет, он прав. Но VBScript «не взлетел». Кроме всего прочего это был стандарт, поддерживаемый одним-единственным браузером — и даже там это была урезанная версия VB, увы.


  1. dom1n1k
    17.06.2019 23:04
    +3

    Статья занятная и полезная, но посыл первого абзаца ложный. Ничего странного, всё работает как задумано и как должно.


  1. kaljan
    18.06.2019 01:45

    вот поэтому и приходится навязывать использование линтеров, который сразу завернет такой вызов, сказав что требуется указать значение параметра radix


    1. khim
      18.06.2019 02:40

      Но ведь radix там указан при вызове… бессмысленный — но указан.


  1. Joric
    18.06.2019 07:51
    -1

    Ага, а [1, 2, 5, 10].sort() выдает [1, 10, 2, 5] по той же самой причине — по которой нормальные люди на этом языке не пишут и как настоящий язык его не воспринимают.


    1. nomn
      18.06.2019 11:11

      Вот бы можно было зайти на MDN и узнать, что оказывается, сортировку можно (нужно) выполнять по правилам собственной функции, а по-умолчанию (без аргументов) сравнение идет с приведением значений к строкам, по их UTF-кодам. Но кто же читает инструкции документацию?!


  1. gadfi
    18.06.2019 10:50
    +1

    image


  1. nomn
    18.06.2019 11:01

    В последние годы, на фоне популярности JS стало откровенно модно критиковать его. Критика — это здорово, когда она по делу, а не ради хохмы. Пример из статьи не кажется жизнеспособным. Согласен с i360u

    Внезапно:

    > ['1', '7', '11'].map(i => parseInt(i))
    < (3) [1, 7, 11]
    


  1. xGromMx
    18.06.2019 14:23

    И для этого нужна целая статья? Люди уже совсем отупели, что не могу просто mdn или спеку почитать?



  1. dabgaryan
    18.06.2019 22:58

    Спасибо за пост, довольно интересно, и я думаю, что я должен включить это в интервью


  1. YemSalat
    19.06.2019 20:34

    На мой взгляд — статья дурацкая. Да, в JS `map` передает несколько аргументов — это нормально. То что у parseInt несколько аргументов — тожe нормально.
    Проблема в том, что parseInt используют не по назначению. В основном чтобы «преобразовать строку в число», но проблема в том, что Int — это не просто число, это Integer. Просто загуглите «parseInt not working with decimal» чтобы понять что куча новичков использует его, даже не понимая значения этой функции.
    parseInt, лично по моему опыту — очень узкоспециализированная штука, надо просто вместо него в туториалах использовать `Number(string)`


    1. DistortNeo
      19.06.2019 20:49

      Дык parseInt и надо использовать, если мы хотим получить целое число, а не просто число.
      И чаще всего хочется видеть именно целое.


      1. YemSalat
        19.06.2019 20:52

        чаще всего хочется видеть именно целое

        Приведите хоть один пример, который встречается условно часто.

        Обычная ситуация — вы ожидаете числа, а апи присылает строки. Вам не нужны конкретно целые числа, a просто нужно сделать числа из ответа апи.


  1. v_m_smith
    19.06.2019 20:45

    поэтому и придумывают языки, которые умеют в JS, например, ClojureScript:

    (map js/parseInt ["1", "7", "11"])

    => (1 7 11)


  1. xel
    20.06.2019 12:25

    Спустя восемь лет нас удивляют всё те же вещи )

    я в своё время придумал на основе этой чуть более хитрую задачу — объяснить почему этот «фикс» не сработал для нулевого элемента массива

    ['1', '7', '11', '19', '29', '33'].map(parseInt.bind(null, 10))
    //[NaN, 7, 11, 19, 29, 33]
    


    1. khim
      20.06.2019 20:50
      +1

      Спустя восемь лет нас удивляют всё те же вещи )
      Дерьмо остаётся дерьмом и через восемь лет и через восемьдесят.

      P.S. Ответ на вопрос с bind становится резко понятен при замене '33' на '333', например…