This question already has answers here:
Get the element with the highest occurrence in an array
(42 answers)
Closed 4 years ago.
I am a beginner in JavaScript and I was trying to write code for finding the mode. My code is running but it can find the mode only when it is written consecutively. But when there is an array like this a = [1,2,3,4,5,2], it can not find the mode.
As I am a beginner I do not want to write anything complex but want to learn it in the simplest way. Can anyone please help me in this purpose?
list = [1,2,3,4,5,6,7,7]
var empty = []
i = 0
max = 0
while (i<list.length){
if (list[i]==list[i+1]){
empty = list[i]
i += 1
}else{
i +=1
}
}
document.write(empty)
Your code assumes that the parameter array is pre-sorted which is a risky and limiting assumption, and only appears to work on sorted arrays (counterexample: [1,1,1,7,7] incorrectly reports 7 as the mode).
If you wish you persist with this approach, you're on the right track, but you'll need to keep track of the current/best streaks, current/best elements and perform a final check for longest streak before returning the result:
var mode = a => {
a = a.slice().sort((x, y) => x - y);
var bestStreak = 1;
var bestElem = a[0];
var currentStreak = 1;
var currentElem = a[0];
for (let i = 1; i < a.length; i++) {
if (a[i-1] !== a[i]) {
if (currentStreak > bestStreak) {
bestStreak = currentStreak;
bestElem = currentElem;
}
currentStreak = 0;
currentElem = a[i];
}
currentStreak++;
}
return currentStreak > bestStreak ? currentElem : bestElem;
};
console.log(mode([1,2,3,4,5,6,7,7]));
console.log(mode([1,1,1,4,5,6,7,7]));
console.log(mode([1,2,3,3,3,6,3,7]));
console.log(mode([1,3,3,4,5,2,2,1]));
console.log(mode([]));
Having said that, sorting is a non-linear operation, so I recommend trying another approach.
The idea is to keep a count of occurrences for each item in the array using an object, then take the element with the highest count. I used reduce to perform these two operations:
const mode = a =>
Object.values(
a.reduce((count, e) => {
if (!(e in count)) {
count[e] = [0, e];
}
count[e][0]++;
return count;
}, {})
).reduce((a, v) => v[0] < a[0] ? a : v, [0, null])[1];
;
console.log(mode([1,2,3,4,5,6,7,7]));
console.log(mode([1,1,1,4,5,6,7,7]));
console.log(mode([1,2,3,3,3,6,3,7]));
console.log(mode([1,3,3,4,5,2,2,1]));
console.log(mode([]));
Or, the same thing, written without reduce for readability:
const mode = a => {
const count = {};
a.forEach(e => {
if (!(e in count)) {
count[e] = 0;
}
count[e]++;
});
let bestElement;
let bestCount = 0;
Object.entries(count).forEach(([k, v]) => {
if (v > bestCount) {
bestElement = k;
bestCount = v;
}
});
return bestElement;
};
console.log(mode([1,2,3,4,5,6,7,7]));
console.log(mode([1,1,1,4,5,6,7,7]));
console.log(mode([1,2,3,3,3,6,3,7]));
console.log(mode([1,3,3,4,5,2,2,1]));
console.log(mode([]));
Note that these approaches don't choose the same mode in case of ties. You may wish to add an array to keep track of all modes, or change your algorithm to pick the first or last occurring mode to suit your needs.
use a hash
list = [1,2,3,4,5,6,7,7]
counts = {}
list.forEach(function(e) {
if(counts[e] === undefined) {
counts[e] = 0
}
counts[e] += 1
})
which results in this:
{1:1,2:1,3:1,4:1,5:1,6:1,7:2}
This related question deals with finding the max and min in a hash, which is essentially what you do at the end of this.
Fast way to get the min/max values among properties of object
Related
let x=[1,2,6,3,5,5,5,4,4];
let y=[3,4,3,5,2,4,4,2,6];
expected_x=[1,2,6,3,5,5,4]
expected_y=[3,4,3,5,2,4,6]
Think of x and y as coordinates.[1,3] will be first point and [4,6] will be last point.
If a [X,Y] has duplicates, only one of the [X,Y] will be displayed in the expected output (no duplicate). And if, there is a mirror like [X,Y] which is a mirror of [Y,X] with both at the same index.
This is the code I have written for just one array to make the array unique. However, I am unsure on how to use it with 2 seperate arrays representing x and y coordinates. Any help will be appreciated :)
let chars = ['A', 'B', 'A', 'C', 'B'];
let uniqueChars = [...new Set(chars)];
console.log(uniqueChars);
Use this:
let x=[1,2,6,3,5,5,5,4,4];
let y=[3,4,3,5,2,4,4,2,6];
const coordinates = [];
let i = -1;
while ( x[++i] ) {
const c = {
index: i,
value: [x[i], y[i]]
}
coordinates.push(c);
}
const coordArray = coordinates.reduce((p, next) => {
if (!p.values.includes(JSON.stringify(next.value)) && !p.values.includes(JSON.stringify([...next.value].reverse()))) {
p.values.push(JSON.stringify(next.value));
p.indexes.push(next.index);
}
return p;
},{
indexes: [],
values: []
})
coordArray.values = coordArray.values.map(JSON.parse)
console.log(coordArray)
You can use a for loop and iterate both arrays together, since they have the same length (being an x,y pair) to each other.
You can also keep a "history" of duplicates and mirrors. Then all you need to do while iterating is check the history. If there is no match, append the current to the result arrays, then update the history.
let x=[1,2,6,3,5,5,5,4,4];
let y=[3,4,3,5,2,4,4,2,6];
let h=[]; // history
let rx = []; // result x
let ry = []; // result y
for (let i = 0; i < x.length && i < y.length; i++) {
// The if line (with include()) would be nice if it worked, but it didn't because of
// always returning false.
// Instead I will have to manually search.
// if (h.includes([x[i], y[i]]) || h.includes([y[i], x[i]])) {
let found = false;
for (let s = 0; s < h.length; s++) {
// check for duplicate
if (h[s][0] == x[i] && h[s][1] == y[i]) {
found = true;
break;
}
// check for mirror
if (h[s][0] == y[i] && h[s][1] == x[i]) {
found = true;
break;
}
}
if (found) {
// do nothing, its a duplicate or mirror
console.log("duplicate or mirror detected on index " + i);
}
else {
// update results
rx.push(x[i]);
ry.push(y[i]);
// update history
h.push([ x[i], y[i] ]);
}
}
console.log("rx: " + rx);
console.log("ry: " + ry);
In short, .include() would have been nice, but apparantly the array by reference broke my intended logic. I don't know. But the above separated those concerns out by a literal search of "history", which would alter the "found" boolean to know whether a duplicate or mirror existed.
Obviously this code could like be shortened into less than 10 or 7 lines, but I wanted to work on it because it was interesting and the approach used demonstrates how regular for loops could be used to solve such "iteration" problems.
Hopes it helps.
First of all, I'm a total beginner in coding and have started a few weeks back, as an persona/intellectual challenge.
I want to know why do I get a null array in this simple script. I think it has something to do with var "i", but I can't find where the error is.
function DivEsc() {
var ssIn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input");
var ssOut = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Output");
var esc = ssIn.getRange(1,3).getValue(); //EscaƱos en reparto
var i = 0;
var arr = [400, 200,100,50];
var newArr = []
newArr = arr.forEach(num => {
for (var i = 1; i <= esc; i++){
return (num/i)
// newArr.push(num)
}
newArr.push(num/i)
}
)
Logger.log(newArr)
}
Array.prototype.forEach does not return a value, newArr will therefore become undefined. JavaScript is a dynamic language where the reassignment of values - even if they don't have the same type - is possible.
Further you return in the first iteration of the for loop, meaning that only the value for num / 1 will be calculated; probably not what you want.
// newArr is now undefined!
newArr = arr.forEach(num => {
for (var i = 1; i <= esc; i++){
// If you return a value here, the loop will stop after the first round with i = 1!
return (num/i)
// newArr.push(num)
}
newArr.push(num/i)
}
)
You could just directly push into newArr to make it work.
arr.forEach(num => {
for(var i = 1; i <= esc; i++) {
newArr.push(num / i);
}
)
If you want to learn more to try out different ways to implement your algorithm, look into JavaScript's functional array methods as e.g. Array.prototype.map and Array.prototype.reduce.
On a side note: Try using an editor/IDE that handles formatting code for you. In your snippet the formatting (e.g. end of the forEach call) makes it hard to reason about the code.
I am new to javascript. Now, I want to make comparison of two website pair by iterating two string array as a pair the same time. These two string array's length are the same. I have searched for sites but didn't find the way to do in javascript. For example, in python, people can do this by using zip(), referencing from
How to merge lists into a list of tuples?.
However, in javascript, I try to something similar to that, but it will iterate over the second list every time it iterate over the element of first list, which is not want I wanted.
codes not what I expected
var FistList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
FirstList.forEach(firstListItem => {
SecondList.forEach(secondListItem => {
//do comparison for these two websites....
});
});
What I expect is to do comparison pair by pair, which is =>
first loop: do comparison of 'https://test1-1/travel' and 'https://test1-2/travel'
second loop: do comparison of 'https://test1-1/cook' and 'https://test1-2/cook'
third loop: do comparison of 'https://test1-1/eat' and 'https://test1-2/eat'
I searched for a whole day but cannot find the way to do in javascript. Please advise. Thanks in advance!
If all you want is to compare values in same position of each array just use the index argument of forEach to reference array element in other array
var FirstList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-1/cook','https://test1-1/eat'];
FirstList.forEach((str, i) => console.log(str === SecondList[i]))
I think a very similar question was already answered here: How to compare arrays in JavaScript?
Accepted answer (https://stackoverflow.com/a/14853974/1842205) describes in depth how you could achieve such a goal.
JavaScript lacks of such a feature like mentioned zip() method from Python. But we have something like prototyping in JS :). And you can create 2D array like below:
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while(i--) arr[length-1 - i] = createArray.apply(this, args);
}
return arr;
}
Array.prototype.zip = function (secondArr) {
let result = createArray(secondArr.length, 2);
for (let i = 0; i < this.length; i++) {
result[i][0] = this[i];
result[i][1] = secondArr[i];
}
return result;
};
// usage
var FistList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
console.log(JSON.stringify(FistList.zip(SecondList)));
I like the OP's idea of making a more functional solution using zip, which can be home-rolled or reused from loadash or underscore.
const firstArray=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
const secondArray=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
const zipped = _.zip(firstArray, secondArray)
const compared = zipped.map(([first, second]) => first === second)
console.log(compared)
// reduce the pairwise comparison to a single bool with every()
// depends on requirements, but probably true iff every comparison is true
const arraysMatch = compared.every(e => e)
console.log(arraysMatch)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
Note that more functional solutions often involve the creation of some intermediate arrays (aka garbage) which is fine for small inputs.
I think the purpose of a forEach loop is to iterate over 1 list only. I would consider using a generic for loop to serve this purpose.
EDIT: I edited the code, and added a string prototype function to calculate the Levenstein distance between 2 strings. It's not rigid to detect for an edit in the exact spot your strings are changed in the examples. But I expect the examples are probably not totally reflective of your real data anyway, so instead of giving you some questionable regex, I'm giving you Levenstein and hope you understand it doesn't care where the difference is, it just cares how much has changed. In the example I only allow 1 character or less of difference: if (diff <= 1) {
//Define a string function for Levenstein Edit Distance
//call it "distancefrom" for clarity
String.prototype.distancefrom = function(string) {
var a = this, b = string + "", m = [], i, j, min = Math.min;
if (!(a && b)) return (b || a).length;
for (i = 0; i <= b.length; m[i] = [i++]);
for (j = 0; j <= a.length; m[0][j] = j++);
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
m[i][j] = b.charAt(i - 1) == a.charAt(j - 1)
? m[i - 1][j - 1]
: m[i][j] = min(
m[i - 1][j - 1] + 1,
min(m[i][j - 1] + 1, m[i - 1 ][j] + 1))
}
}
return m[b.length][a.length];
}
//Your Code
var FirstList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat', 'https://waffles.domain/syrup', 'http://pancakes.webpresence/butter'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat', 'https://waffles.domain/syrups', 'https://pancakes.webpresence/buttery'];
for (let i=0; i < FirstList.length; i++) {
let diff = FirstList[i].distancefrom(SecondList[i]);
console.log('"'+FirstList[i]+'" is different than "'+SecondList[i]+'" by '+diff+' characters');
if (diff <= 1) {
console.log('Since its less than 1 character of difference, it would technically Pass our test.');
} else {
console.log('Since its more than 1 character of difference, it would Fail our test!');
}
console.log('-----------------');
}
References:
Levenstin Gist by scottgelin on GitHub
I am working on dictionary application written with react-native.
When I want to filter the array from the search box, I wrote below function. This is working quite good when I test with 2000 word list. But when the word list goes to thousands the search speed is really slow.
So, how can I improve this search function?
//Filter array when input text (Search)
let filteredWords = []
if(this.state.searchField != null)
{
filteredWords = this.state.glossaries.filter(glossary => {
return glossary.word.toLowerCase().includes(this.state.searchField.toLowerCase());
})
}
There are multiple factors that are making this code slow:
You're using filter() with a lambda. This adds a function call overhead for each item being searched.
You're calling toLowercase() on both strings before calling includes(). This will allocate two new string objects for every comparison.
You're calling includes. For some reason the includes() method is not as well optimized in some browsers as indexOf().
for loop (-11%)
Instead of using the filter() method, I recommend creating a new Array and using a for loop to fill it.
const glossaries = this.state.glossaries;
const searchField = this.state.searchField;
const filteredWords = [];
for (let i = 0; i < glossaries.length; i++) {
if (glossaries[i].toLowerCase().includes(searchField.toLowerCase())) {
filteredWords.push(glossaries[i]);
}
}
toLowerCase allocations (-45%)
Memory allocation is expensive due to the fact that JavaScript uses garbage collection mechanism for freeing used memory. When a garbage collection is performed the whole program is paused while it tries to finds memory which is not used anymore.
You can get rid of the toLowerCase() (inside the search loop) completely by making a copy of the glossary everytime the glossary is updated, which I assume is not often.
// When you build the glossary
this.state.glossaries = ...;
this.state.searchGlossaries = this.state.glossaries.map(g => g.toLowerCase());
You can also remove the toLowerCase() on the searchText by calling it once before the loop. After these changes, the code will look like:
const glossaries = this.state.glossaries;
const searchGlassaries = this.state.searchGlossaries;
const searchField = this.state.searchField.toLowerCase();
const filteredWords = [];
for (let i = 0; i < glossaries.length; i++) {
if (searchGlassaries[i].includes(searchField)) {
filteredWords.push(glossaries[i]);
}
}
indexOf() instead of includes() (-13%)
I am not really sure why this is the case, but tests show that indexOf is a lot faster than includes.
const glossaries = this.state.glossaries;
const searchGlassaries = this.state.searchGlossaries;
const searchField = this.state.searchField.toLowerCase();
const filteredWords = [];
for (let i = 0; i < glossaries.length; i++) {
if (searchGlassaries[i].indexOf(searchField) !== -1) {
filteredWords.push(glossaries[i]);
}
}
Overall the performance has improved by 70%.
I got the performance percentages from https://jsperf.com/so-question-perf
Optimize the algorithm
In the comments you said you would like an example of optimizations that can be done when the requirements are loosened to only match words that start with the search text. One way to do this is a binary search.
Let's take the code from above as starting point. We sort the glossaries before we store it in the state. For sorting case insensitively, JavaScript exposes the Intl.Collator constructor. It provides the compare(x, y) method that returns:
negative value | X is less than Y
zero | X is equal to Y
positive value | X is greater than Y
And the resulting code:
// Static in the file
const collator = new Intl.Collator(undefined, {
sensitivity: 'base'
});
function binarySearch(glossaries, searchText) {
let lo = 0;
let hi = glossaries.length - 1;
while (lo <= hi) {
let mid = (lo + hi) / 2 | 0;
let comparison = collator.compare(glossaries[mid].word, searchText);
if (comparison < 0) {
lo = mid + 1;
}
else if (comparison > 0) {
hi = mid - 1;
}
else {
return mid;
}
}
return -1;
}
// When you build the glossary
this.state.glossaries = ...;
this.state.glossaries.sort(function(x, y) {
return collator.compare(x.word, y.word);
});
// When you search
const glossaries = this.state.glossaries;
const searchField = this.state.searchField.toLowerCase();
const filteredWords = [];
const idx = binarySearch(glossaries, searchField);
if (idx != -1) {
// Find the index of the first matching word, seeing as the binary search
// will end up somewhere in the middle
while (idx >= 0 && collator.compare(glossaries[idx].word, searchField) < 0) {
idx--;
}
// Add each matching word to the filteredWords
while (idx < glossaries.length && collator.compare(glossaries[idx].word, searchField) == 0) {
filteredWords.push(glossaries[idx]);
}
}
As the question doesn't seem to belong on CodeReview, I think there are a few things that you can do to make your code drastically faster [citation needed]:
Cache that call to this.state.searchField.toLowerCase() as you don't need to call it on every iteration.
Use regular old for loops instead of flashy-but-slow Array functions.
And here is the final result:
let filteredWords = []
if(this.state.searchField != null) {
let searchField = this.state.searchField.toLowerCase(),
theArray = this.state.glossaries; // cache this too
for(let i = 0, l = theArray.length; i < l; ++i) {
if(theArray[i].word.toLowerCase().includes(searchField)) {
filteredWords.push(theArray[i]);
}
}
}
Edit:
If you want to search for glossaries whose word start with searchField, then use indexOf === 0 instead of includes as the condition like this:
if(theArray[i].word.toLowerCase().indexOf(searchField) === 0) {
This question already has an answer here:
Find the highest subset of an integer array whose sums add up to a given target
(1 answer)
Closed 6 years ago.
I'm stumbling into a problem that I've to treat a given value and check if this value is bigger than my array of values, if it is, combine the output using my array.
For example.
My array always will be:
const ArrayPrimitive = [100,50,20,10];
And for example the given value in the input:
Entry: 30.00 Result: [20.00, 10.00]
Entry: 80.00 Result: [50.00, 20.00, 10.00]
Entry: 125.00 Result: throw NoteUnavailableException
Entry: -130.00 Result: throw InvalidArgumentException
Entry: NULL Result: [Empty Set]
I've started to develop but I'm stuck in the in how to deal with the rest, after check if the value is valid.
const ArrayPrimitive = [100,50,20,10];
var combination = [];
$(document).ready(function(){
$(".btn-submit").on("click",function(){
var amountOfMoney = document.getElementById('amountOfMoney').value;
for(let i=0; i=ArrayPrimitive.length;i++){
if(amountOfMoney=ArrayPrimitive[i]){
combination.push(amountOfMoney);
}
while(amountOfMoney > ArrayPrimitive[i]){
var k = amountOfMoney/ArrayPrimitive[i];
for(let j=0; j = k; j++){
combination.push(ArrayPrimitive[i]);
amountOfMoney = amountOfMoney - ArrayPrimitive[i];
}
var rest = n % ArrayPrimitive[i];
}
}
});
});
My major questions are:
Can you guys help me to solve this in the old way?
I'm about to learn ES6 and probably the .map or other function could save my life, could someone enlighten me in this point?
I hope I made myself clear.
https://jsfiddle.net/v748ha08/1/
UPDATE: Thanks #Malk for your answer but your solution just provides the right answer only when you don't have to combine multiple values of the subset.
e.g.
Entry: 200.00
Result: [100.00, 100.00]
In this case I need 2 values of 100 in my subset, and when I test this they functions throws me an error.
If you want to iterate through an array and build up an object (or anything else) along the way then Array.reduce() is the way to go.
const ArrayPrimitive = [100, 95, 20, 10]; // Assuming presorted array(descending)
function findChange(m) {
return ArrayPrimitive.reduce((mm, c) => {
if (mm.rest >= c) {
mm.change.push(c);
mm.rest -= c
}
return mm
}, {
change: [],
rest: m
});
}
function findChangeOld(m) {
var retval = {
change: [],
rest: m
},
i = ArrayPrimitive.length;
for (var x = 0; x < i; x++) {
if (retval.rest >= ArrayPrimitive[x]) {
retval.change.push(ArrayPrimitive[x])
retval.rest -= ArrayPrimitive[x];
}
}
return retval;
}
function calcChange(v) {
var c = findChangeOld(v);
if (v < 0 || isNaN(v)) {
console.log(`${v}: throw InvalidArgumentException`);
return;
}
if (c.rest > 0)
console.log(`${v}: throw NoteUnavailableException`);
else
console.log(`${v}: ${c.change}`);
}
calcChange(30);
calcChange(80);
calcChange(105);
calcChange(125);
calcChange(-130);
calcChange(null);