Повторы слов 2/5

Пока кривенько вот так набросал, мб улучшу потом =)

1 вариант - for
 const getRepeats = function(array) {
  let obj = {};
  for (let i = 0; i < array.length; i++) {
      if (!obj[array[i]]) {
        obj[array[i]] = 1;
      } else {
        obj[array[i]]++;
      }
  }  
  return obj;
}
2 вариант - forEach
function getRepeats(arrayOfProperties) {
  let obj = {};
  arrayOfProperties.forEach( prop => obj[prop]? obj[prop]++ : obj[prop] = 1 );
  return obj;
}
3 вариант - reduce
const getRepeats = (arr) => arr.reduce(
  (p, c) => {
    p[c] = (p[c] || 0) + 1;
    return p;
  }, {});
8 лайков

Привет!
спасибо за такую подсказку, я дня 3 ходил вокруг да около, но сам не допёр, пришлось использовать твою подсказку чтобы дальше пройти.
На мой взгляд задача довольно сложная(
вопрос!
почему использовалось let вместо привычного var?
а точнее, где когда используют одно а где другое?
прочитал бегло пару статей, но толком не понял
скажешь - ну прочитай не бегло))) - а смыысла нет для меня, там примеры с какими-то окнами приводятся, пока не проходил окна, не знаю что это)

про let и const можно почитать тут.
уже давно они стали стандартом в индустрии, просто курсы учат ванильному js, который хоть и лежит в основе современного языка, но не применяется уже либо просто кому-то влом переделывать старое. у var на самом деле достаточно всяких косяков, которые жизнь усложняют.

2 лайка

благодарю!
ознакомлюсь с материалом по ссылке

1 лайк

Спасибо за приведенное решение.

Объясните, пожалуйста, как работает условие:

if (!obj[array[i]]) {
        obj[array[i]] = 1;
      } else {
        obj[array[i]]++;
      }

В цикле мы перебираем массив из слов, а как работает само условие «if»?

Не могу понять, как работает вот этот момент:

if (!obj[array[i]])

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

читается так:
если объект не содержит ключ, который называется “элемент массива с индексом i”, то присвоить такому ключу значение 1, иначе инкремент этого ключа.

при всем при этом, если такого ключа не было в объекте, то мы его тут же на месте создаем и присваиваем ему значение.

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

9 лайков

Классное решение! Которое “простой” вариант!
У меня немного по другому реализовано, но смысл примерно тот же. Опирался на уже пройденный материал курса.

var getRepeats = function (array) {
  var object = {};
  for (var i = 0; i < array.length; i++) {
    if (array.indexOf(array[i]) === i) {
      var repeatNumber = 1; 
      for (var j = i+1; j < array.length; j++) {
        if(array[i] === array[j]) {
        repeatNumber++;
        }
      }
    object[array[i]] = repeatNumber;
    }
  }
  return object;
};

Получилось достаточно объемно, а ваше решение просто огонь!

4 лайка

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

что я имею ввиду - у нас будут разные результаты в нестандартных ситуациях. например, попробуйте вызвать функцию с почти пустым массивом, с ненулевой длиной. например [,,,"рыба",,"рыба",,]. как можете удостовериться - длина этого массива - 7.
что из этого правильнее - я хз =) но легче мой скрипт
P.S. Спасибо, я стараюсь для форума =)

6 лайков

Сделал вот так, достаточно легко читается, как мне кажется:

 var getRepeats = function (words) {
     var obj = {};
      
     for (var i = 0; i < words.length; i ++) {
        obj[words[i]] = 0; 
     }
     for (var i = 0; i < words.length; i ++) {
         obj[words[i]] += 1; 
     }
     return(obj);
};

Кто-нибудь может объяснить, почему запись obj[ words[i] ] работает,
а запись obj.words[i] не работает?

2 лайка

первая запись - поиск ключа в объекте с именем “элемент массива”
вторая - поиск в объекте obj ключа words ( который тоже объект), а в нем ключ i

второе вы еще не проходили, это мапа будет

1 лайк

Громоздко, но тем не менее.

var getRepeats = function(words) {
  var result = {};
  var uniqueWords = [];
  
  for (var j = 0; j < words.length; j++) {
    var count = 1;
    
    if (uniqueWords.indexOf(words[j]) >= 0) {
      continue;
    } else {
    for (var i = j; i < words.length; i++) {
      if (words[j] === words[i] && i !== j) {
        count ++;
      }
    }
    result[words[j]] = count;
    uniqueWords.push(words[j]);
    } 
  }
  return result;
}

Я предложу три варианта. Начинаем традиционно с гавнокода:

// 1

var getRepeats = function (data) {
  var obj = {}; // создаём пустой объект
  var usedWords = []; // создаём пустой массив, в который будем записывать слова, которые уже использовались
  
  for (var i = data.length; i--; ){ // цикл от больше к меньшему. Когда i-- === 0, то цикл прекращается
 if (usedWords.indexOf( data[i] ) == -1) { //  Если текущего слова нет в массиве использованных слов,  его индекс === -1, тогда выполняем следующие действия
   usedWords.push(data[i]); // Тогда добавляем это новое слово в массив  использованных слов
   obj[data[i]] = 0; // Также добавляем его в объект со значением 0
   for (var j = data.length; j--; ){ 
 if(data[i] == data[j]) obj[data[i]]++; // Перебираем массив ещё раз и ищем сколько раз встречается слово, и добавляем к свойству объекта единицу
    } // Завершили цикл
  }  // Завершили условие
    } // Завершили цикл
    return obj;
  }

// 2 С точки зрения сайта, вот такое стандартное решение

var getRepeats = function (data) {
  var obj = {};
  for (var i = data.length; i--; ){ 
  obj[data[i]] = 0; 
  for (var j = data.length; j--; ){  
if (data[j] === data[i]) obj[data[i]]++;
    }
    }
    return obj;
  }

// 3 Я тут погуглил, нашёл вот такую хрень массив.filter(x => x === массив[i]).length , и адаптировал под свой скрипт. Работает, но я пока не совсем понимаю как работает.

var getRepeats = function (data) {
  var obj = {};
  for (var i = data.length; i--; ){ 
  obj[data[i]] = data.filter(x => x === data[i]).length;
    }
    return obj;
  }
1 лайк

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

а можете объяснить почему это работает, а не вываливается в Timeout error?

for (var i = data.length; i--; ){ ...}

Насчёт фильтр спасибо, не знал. Про редьюс я ещё ничего не знаю, ни разу это не практиковал.

Разницы между
for (var i = data.length; i--; ){ ...}
и
for (var i = 0; i < data.length;i++){ ...}
с первого взгляда нет,
но в одном из видео на ютубе Sorax сказал, что способ с инркементом медленнее, чем способ с декрементом
Он это объяснил тем, что меньше операций совершается при каждом круге цикла. Программист он не глупый)

Вываливается в Timeout error ? Нет, конечно. Я все скрипты тестировал.

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

нет, я не говорю что вываливается с ошибкой, я спрашиваю почему не вываливается, потому что не понимаю что является условием выхода из цикла. т.е. это норм работает применительно к массивам, так? т.е. в последней итерации он получает arr.length = -1, получает отказ как нереферентное значение (т.к. length от 0 до индекса последнего элемента +1) и спокойно завершает цикл, я так понял.

upd: вроде доперло,
цикл for работает так:
получает первый шаг i = arr.length и это известно до 1 итерации
пост-итерационное действие отсутствует, тут просто. интересна серединка:
далее он проверяет возвращает ли true выражение i = i - 1, т.е. не является ли результат вычисления нулем (грубо)
на последней итерации, поскольку это постфиксный декремент, он в условие возвращает 1, уменьшается на единицу, передает в цикл 0, там совершаются какие-то операции с i == 0 и перед следующей итерацией оно снова проверяет i = i - 1, которое возвращает 0 и завершает цикл. Гениально.
и что интересно, на 1ой итерации цикла происходят операции с последним элементом массива, т.к. в цикл возвращается уже arr.length - 1, что и есть индекс последнего элемента массива.

Ну в JS есть такая фишка, что ноль равен булевому значению false
Поэтому цикл останавливается когда

for (...; 0; ...) {...}
т.е. это равно
for (...; false; ...) {...}

Ну я так понял, что Вам это уже стало понятно) Просто своими словами объяснил

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

for (var i = data.length; --i; ){ ...}

Я прост изучаю уже полгода js, но не видел такой реализации цикла ещё. Что-то новенькое для меня =) для этого собственно и торчу тут на форуме

В ютубе можно много фишек нахвататься) На английском языке смотрите уроки) ахах

Про codewars слышали?

угу, на очереди. пока тут прохожу https://checkio.org/

но честно говоря, мне туда еще рано соваться. как минимум нужно свободно понимать reduce, filter, map и RegExp чтобы не городить километровые простыни кода (как это делают тут на форуме)
а так пока “учуся” xD

1 лайк

мое решение через recude

Сводка
const getRepeats = (arr) => {
  return arr.reduce((acc, el) => {
    acc[el] ? acc[el]++ : acc[el] = 1;
    return acc;
  }, {});
};

сделал решение Hierumo чуть более наглядным