2014-11-12 1 views
4

Я пытаюсь реализовать кусок кода на javascript для анализа слова/частоты на заданной строке. Моя цель состоит в том, чтобы вернуть массив, как:Частота слов для массива ключей/значений на javascript

[{text: firstword, size:3 },{text:secondword , size:5 },{text: nword, size: 1},...] 

Я осуществил следующий код, но я бегу из памяти, так что я не знаю, если его нормально или нет.

function wordFrequency(txt){ 
    var wordArray = txt.split(/[ .?!,*'"]/); 
    var newArray = []; 
    $.each(wordArray, function (ix, word) { 
     if (newArray.length >= 1){ 
      newArray.some(function (w){ 
       if (w.text === word){ 
        w.size++; 
       } else { 
        newArray.push({text: word, size: 1}); 
       } 
      }); 
     } else { 
      newArray.push({text: word, size: 1}); 
     } 
    }); 
    return newArray; 
} 
+0

Это нормально в моей консоли – Nickool

+0

но он повторил результат я попытался этот: wordFrequency («negin! kjs $ wkjh * df oiuw & skdjhh»); – Nickool

+0

Я пытаюсь использовать большой фрагмент текста, и я каждый раз теряю память, даже когда пытался с jsfiddle. :( – diegodacal

ответ

2

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

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

Чтобы сделать это ясно, для каждого слова, которое находится в newArray перед словом, которое вы ищете, вы добавляете новый объект, содержащий ваше слово.

Пусть ваш newArray выглядит следующим образом:

[{word:"test"},{word:"another"},{word:"one"},{word:"more"}]

после вызова вашей функции для слова even это выглядит следующим образом:

[{word:"test"},{word:"another"},{word:"one"},{word:"more"},{word:"even"},{word:"even"},{word:"even"},{word:"even"}]

Использование Array.prototype.filter было бы лучшим подходом здесь , найдя подходящий элемент, обратите внимание, что я также заменил $.each на Array.prototype.forEach:

function wordFrequency(txt){ 
 
    var wordArray = txt.split(/[ .?!,*'"]/); 
 
    var newArray = [], wordObj; 
 
    wordArray.forEach(function (word) { 
 
    wordObj = newArray.filter(function (w){ 
 
     return w.text == word; 
 
    }); 
 
    if (wordObj.length) { 
 
     wordObj[0].size += 1; 
 
    } else { 
 
     newArray.push({text: word, size: 1}); 
 
    } 
 
    }); 
 
    return newArray; 
 
} 
 
document.write(JSON.stringify(wordFrequency("count everything, count all the words, count all the words!").sort(function(a,b){return a.size<b.size})).split("},").join("}<br/>"));

1

Рассказывать частоты все, что вам нужно, это хэш-карта подход. Ваш алгоритм квадратичен, так как метод some вложен в метод each, поэтому вы всегда перебираете newArray, чтобы найти запись и увеличить размер.

Подход к карте легко достижим с использованием объекта JavaScript. Это также дает вам постоянное время поиска, которое лучше, чем подход вложенных циклов.

Попробуйте этот подход вместо:

function wordFrequency(txt){ 
    var wordArray = txt.split(/[ .?!,*'"]/); 
    var map = {}; 
    $.each(wordArray, function(ix, word) { 
     // skip empty results 
     if (!word.length) { 
     return; 
     } 
     // add word to map 
     if (!map[word]) { 
     map[word] = 0; 
     } 
     map[word]++; 
    }); 
    return map; 
} 

Для использования функции:

var text = "hello!world*hello foo 'bar'foo"; 
var result = wordFrequency(text); 

// iterate over results 
Object.keys(result).forEach(function(w) { 
    console.log(w + ": " + result[w]); 
}); 

// or use for...in 
for (var w in result) { 
    console.log(w + ": " + result[w]); 
} 

Если вы действительно хотите, вы можете затем отобразить результат в нужный формат массива с текстом и размерных свойств :

var mappedResult = Object.keys(result).map(function(w) { 
    return { text: w, size: result[w] }; 
}); 
console.log(mappedResult); 

Кроме того, в зависимости от ваших целевых браузеров вы можете рассмотреть возможность использования g массив forEach вместо jQuery $.each, аналогичный тому, что я сделал с участком Object.keys.

Вот JSBin example.

+0

Возможно, лучше использовать wordArray.forEach (function (word) {}); – htmlfarmer

1

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

function wordFrequency(txt) { 
    var words = txt.split(/[ \.\?!,\*'"]+/), 
     seen = []; 
    for (var i = 0; i < words.length; i++) { 
     var w = words[i], 
      found = false; 
     for (var j = 0; j < seen.length; j++) { 
      if (w === seen[j].text) { 
       seen[j].size++; 
       found = true; 
       break; 
      } 
     } 
     if (!found) seen.push({ text: w, size: 1 }); 
    } 
    return seen; 
} 

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

И вот эквивалент с использованием Array.prototype.forEach() и Array. prototype.indexOf(), но мы должны добавить еще один промежуточный стек результатов для последнего. Поэтому нам нужно добавить еще одну итерацию, чтобы получить окончательный результат. (Мы не должны делать это с помощью Array.prototype.findIndex(), но это не является стандартным методом.)

function wordFrequency2(txt) { 
    var words = txt.split(/[ \.\?!,\*'"]+/), 
     seen = [], 
     freq = []; 
    // get frequencies 
    words.forEach(function (w) { 
     var idx = seen.indexOf(w); 
     if (idx >= 0) { 
      freq[idx]++; 
     } 
     else { 
      seen.push(w); 
      freq.push(1); 
     } 
    }); 
    // produce the results array 
    var r = []; 
    seen.forEach(function (w, idx) { 
     r.push({ text: w, size: freq[idx] }); 
    }); 
    return r; 
} 

Положив оптимизации во внимание, первая версия с использованием явных петель будет, вероятно, выполнять быстрее ...

+0

то, что на самом деле это те 'seen.push' и' freq.lines', которые должны быть Делать ?! Последний должен быть 'freq [w] = 1', а позже вы можете использовать juse' Object.keys (freq) ', чтобы получить список уникальных слов. – Alnitak

+0

@Alnitak Это предполагало, что порядок будет любое значение. Поскольку мы не знаем ни одного из вариантов использования, мы не имеем права отказываться от этой информации (как это было бы при использовании ассоциативных массивов/хэшей). Я бы предположил, что это будет иметь значение, так как проблема довольно тривиальна с использованием объектных ключей. (если (см. [w]) freq [w] ++;) – masswerk

+0

ok, я понимаю, что вы пытаетесь сделать сейчас, хотя в результате вы все равно получаете алгоритм «O (n * m)». – Alnitak

2

Было бы проще и далеким более эффективно создавать прямую карту от слова к частоте и только потом преобразовывать ее в структуру массива. Дан массив words создать карту слов:

var freq = words.reduce(function(p, c) { 
    p[c] = (p[c] || 0) + 1; 
    return p; 
}, {}); 

и новообращенный, отображающую в массив:

var array = Object.keys(freq).map(function(key) { 
    return { text: key, size: freq[key] }; 
}); 
-1
var words = (function(){ 

var sWords = document.body.innerText.toLowerCase().trim().replace(/[,;.]/g,'').split(/[\s\/]+/g).sort(); 
var iWordsCount = sWords.length; // count w/ duplicates 

// array of words to ignore 
var ignore = ['and','the','to','a','of','for','as','i','with','it','is','on','that','this','can','in','be','has','if']; 
ignore = (function(){ 
var o = {}; // object prop checking > in array checking 
var iCount = ignore.length; 
for (var i=0;i<iCount;i++){ 
o[ignore[i]] = true; 
} 
return o; 
}()); 

var counts = {}; // object for math 
for (var i=0; i<iWordsCount; i++) { 
var sWord = sWords[i]; 
if (!ignore[sWord]) { 
counts[sWord] = counts[sWord] || 0; 
counts[sWord]++; 
} 
} 

var arr = []; // an array of objects to return 
for (sWord in counts) { 
arr.push({ 
text: sWord, 
frequency: counts[sWord] 
}); 
} 

// sort array by descending frequency | http://stackoverflow.com/a/8837505 
return arr.sort(function(a,b){ 
return (a.frequency > b.frequency) ? -1 : ((a.frequency < b.frequency) ? 1 : 0); 
}); 

}()); 

(function(){ 
var iWordsCount = words.length; // count w/o duplicates 
for (var i=0; i<iWordsCount; i++) { 
var word = words[i]; 
console.log(word.frequency, word.text); 
} 
}());