Related
I'm looking for an elegant way of determining which element has the highest occurrence (mode) in a JavaScript array.
For example, in
['pear', 'apple', 'orange', 'apple']
the 'apple' element is the most frequent one.
This is just the mode. Here's a quick, non-optimized solution. It should be O(n).
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
There have been some developments in javascript since 2009 - I thought I'd add another option. I'm less concerned with efficiency until it's actually a problem so my definition of "elegant" code (as stipulated by the OP) favours readability - which is of course subjective...
function mode(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
mode(['pear', 'apple', 'orange', 'apple']); // apple
In this particular example, should two or more elements of the set have equal occurrences then the one that appears latest in the array will be returned. It's also worth pointing out that it will modify your original array - which can be prevented if you wish with an Array.slice call beforehand.
Edit: updated the example with some ES6 fat arrows because 2015 happened and I think they look pretty... If you are concerned with backwards compatibility you can find this in the revision history.
As per George Jempty's request to have the algorithm account for ties, I propose a modified version of Matthew Flaschen's algorithm.
function modeString(array) {
if (array.length == 0) return null;
var modeMap = {},
maxEl = array[0],
maxCount = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
maxEl += "&" + el;
maxCount = modeMap[el];
}
}
return maxEl;
}
This will now return a string with the mode element(s) delimited by a & symbol. When the result is received it can be split on that & element and you have your mode(s).
Another option would be to return an array of mode element(s) like so:
function modeArray(array) {
if (array.length == 0) return null;
var modeMap = {},
maxCount = 1,
modes = [];
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
return modes;
}
In the above example you would then be able to handle the result of the function as an array of modes.
Based on Emissary's ES6+ answer, you could use Array.prototype.reduce to do your comparison (as opposed to sorting, popping and potentially mutating your array), which I think looks quite slick.
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
I'm defaulting to null, which won't always give you a truthful response if null is a possible option you're filtering for, maybe that could be an optional second argument
The downside, as with various other solutions, is that it doesn't handle 'draw states', but this could still be achieved with a slightly more involved reduce function.
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
if(b[k]) b[k]++; else b[k]=1;
if(maxi < b[k]) { max=k; maxi=b[k] }
}
As I'm using this function as a quiz for the interviewers, I post my solution:
const highest = arr => (arr || []).reduce( ( acc, el ) => {
acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1
acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el
return acc
}, { k:{} }).max
const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2]
console.log(highest(test))
Trying out a declarative approach here. This solution builds an object to tally up the occurrences of each word. Then filters the object down to an array by comparing the total occurrences of each word to the highest value found in the object.
const arr = ['hello', 'world', 'hello', 'again'];
const tally = (acc, x) => {
if (! acc[x]) {
acc[x] = 1;
return acc;
}
acc[x] += 1;
return acc;
};
const totals = arr.reduce(tally, {});
const keys = Object.keys(totals);
const values = keys.map(x => totals[x]);
const results = keys.filter(x => totals[x] === Math.max(...values));
This solution has O(n) complexity:
function findhighestOccurenceAndNum(a) {
let obj = {};
let maxNum, maxVal;
for (let v of a) {
obj[v] = ++obj[v] || 1;
if (maxVal === undefined || obj[v] > maxVal) {
maxNum = v;
maxVal = obj[v];
}
}
console.log(maxNum + ' has max value = ' + maxVal);
}
findhighestOccurenceAndNum(['pear', 'apple', 'orange', 'apple']);
For the sake of really easy to read, maintainable code I share this:
function getMaxOcurrences(arr = []) {
let item = arr[0];
let ocurrencesMap = {};
for (let i in arr) {
const current = arr[i];
if (ocurrencesMap[current]) ocurrencesMap[current]++;
else ocurrencesMap[current] = 1;
if (ocurrencesMap[item] < ocurrencesMap[current]) item = current;
}
return {
item: item,
ocurrences: ocurrencesMap[item]
};
}
Hope it helps someone ;)!
Here’s the modern version using built-in maps (so it works on more than things that can be converted to unique strings):
'use strict';
const histogram = iterable => {
const result = new Map();
for (const x of iterable) {
result.set(x, (result.get(x) || 0) + 1);
}
return result;
};
const mostCommon = iterable => {
let maxCount = 0;
let maxKey;
for (const [key, count] of histogram(iterable)) {
if (count > maxCount) {
maxCount = count;
maxKey = key;
}
}
return maxKey;
};
console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
Time for another solution:
function getMaxOccurrence(arr) {
var o = {}, maxCount = 0, maxValue, m;
for (var i=0, iLen=arr.length; i<iLen; i++) {
m = arr[i];
if (!o.hasOwnProperty(m)) {
o[m] = 0;
}
++o[m];
if (o[m] > maxCount) {
maxCount = o[m];
maxValue = m;
}
}
return maxValue;
}
If brevity matters (it doesn't), then:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
return mV;
}
If non–existent members are to be avoided (e.g. sparse array), an additional hasOwnProperty test is required:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
if (a.hasOwnProperty(i)) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
}
return mV;
}
getMaxOccurrence([,,,,,1,1]); // 1
Other answers here will return undefined.
Here is another ES6 way of doing it with O(n) complexity
const result = Object.entries(
['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => {
if (previous[current] === undefined) previous[current] = 1;
else previous[current]++;
return previous;
}, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0];
console.log("Max value : " + result);
function mode(arr){
return arr.reduce(function(counts,key){
var curCount = (counts[key+''] || 0) + 1;
counts[key+''] = curCount;
if (curCount > counts.max) { counts.max = curCount; counts.mode = key; }
return counts;
}, {max:0, mode: null}).mode
}
Another JS solution from: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php
Can try this too:
let arr =['pear', 'apple', 'orange', 'apple'];
function findMostFrequent(arr) {
let mf = 1;
let m = 0;
let item;
for (let i = 0; i < arr.length; i++) {
for (let j = i; j < arr.length; j++) {
if (arr[i] == arr[j]) {
m++;
if (m > mf) {
mf = m;
item = arr[i];
}
}
}
m = 0;
}
return item;
}
findMostFrequent(arr); // apple
This solution can return multiple elements of an array in case of a tie. For example, an array
arr = [ 3, 4, 3, 6, 4, ];
has two mode values: 3 and 6.
Here is the solution.
function find_mode(arr) {
var max = 0;
var maxarr = [];
var counter = [];
var maxarr = [];
arr.forEach(function(){
counter.push(0);
});
for(var i = 0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
if(arr[i]==arr[j])counter[i]++;
}
}
max=this.arrayMax(counter);
for(var i = 0;i<arr.length;i++){
if(counter[i]==max)maxarr.push(arr[i]);
}
var unique = maxarr.filter( this.onlyUnique );
return unique;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
};
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
const frequence = (array) =>
array.reduce(
(acc, item) =>
array.filter((v) => v === acc).length >=
array.filter((v) => v === item).length
? acc
: item,
null
);
frequence([1, 1, 2])
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17],
c = {}, // counters
s = []; // sortable array
for (var i=0; i<array.length; i++) {
c[array[i]] = c[array[i]] || 0; // initialize
c[array[i]]++;
} // count occurrences
for (var key in c) {
s.push([key, c[key]])
} // build sortable array from counters
s.sort(function(a, b) {return b[1]-a[1];});
var firstMode = s[0][0];
console.log(firstMode);
Here is my solution to this problem but with numbers and using the new 'Set' feature. Its not very performant but i definitely had a lot of fun writing this and it does support multiple maximum values.
const mode = (arr) => [...new Set(arr)]
.map((value) => [value, arr.filter((v) => v === value).length])
.sort((a,b) => a[1]-b[1])
.reverse()
.filter((value, i, a) => a.indexOf(value) === i)
.filter((v, i, a) => v[1] === a[0][1])
.map((v) => v[0])
mode([1,2,3,3]) // [3]
mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]
By the way do not use this for production this is just an illustration of how you can solve it with ES6 and Array functions only.
const mode = (str) => {
return str
.split(' ')
.reduce((data, key) => {
let counter = data.map[key] + 1 || 1
data.map[key] = counter
if (counter > data.counter) {
data.counter = counter
data.mode = key
}
return data
}, {
counter: 0,
mode: null,
map: {}
})
.mode
}
console.log(mode('the t-rex is the greatest of them all'))
Here is my solution :-
function frequent(number){
var count = 0;
var sortedNumber = number.sort();
var start = number[0], item;
for(var i = 0 ; i < sortedNumber.length; i++){
if(start === sortedNumber[i] || sortedNumber[i] === sortedNumber[i+1]){
item = sortedNumber[i]
}
}
return item
}
console.log( frequent(['pear', 'apple', 'orange', 'apple']))
Try it too, this does not take in account browser version.
function mode(arr){
var a = [],b = 0,occurrence;
for(var i = 0; i < arr.length;i++){
if(a[arr[i]] != undefined){
a[arr[i]]++;
}else{
a[arr[i]] = 1;
}
}
for(var key in a){
if(a[key] > b){
b = a[key];
occurrence = key;
}
}
return occurrence;
}
alert(mode(['segunda','terça','terca','segunda','terça','segunda']));
Please note that this function returns latest occurence in the array
when 2 or more entries appear same number of times!
With ES6, you can chain the method like this:
function findMostFrequent(arr) {
return arr
.reduce((acc, cur, ind, arr) => {
if (arr.indexOf(cur) === ind) {
return [...acc, [cur, 1]];
} else {
acc[acc.indexOf(acc.find(e => e[0] === cur))] = [
cur,
acc[acc.indexOf(acc.find(e => e[0] === cur))][1] + 1
];
return acc;
}
}, [])
.sort((a, b) => b[1] - a[1])
.filter((cur, ind, arr) => cur[1] === arr[0][1])
.map(cur => cur[0]);
}
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple']));
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple', 'pear']));
If two elements have the same occurrence, it will return both of them. And it works with any type of element.
// O(n)
var arr = [1, 2, 3, 2, 3, 3, 5, 6];
var duplicates = {};
max = '';
maxi = 0;
arr.forEach((el) => {
duplicates[el] = duplicates[el] + 1 || 1;
if (maxi < duplicates[el]) {
max = el;
maxi = duplicates[el];
}
});
console.log(max);
I came up with a shorter solution, but it's using lodash. Works with any data, not just strings. For objects can be used:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el.someUniqueProp)), arr => arr.length)[0];
This is for strings:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el)), arr => arr.length)[0];
Just grouping data under a certain criteria, then finding the largest group.
Here is my way to do it so just using .filter.
var arr = ['pear', 'apple', 'orange', 'apple'];
function dup(arrr) {
let max = { item: 0, count: 0 };
for (let i = 0; i < arrr.length; i++) {
let arrOccurences = arrr.filter(item => { return item === arrr[i] }).length;
if (arrOccurences > max.count) {
max = { item: arrr[i], count: arrr.filter(item => { return item === arrr[i] }).length };
}
}
return max.item;
}
console.log(dup(arr));
Easy solution !
function mostFrequentElement(arr) {
let res = [];
for (let x of arr) {
let count = 0;
for (let i of arr) {
if (i == x) {
count++;
}
}
res.push(count);
}
return arr[res.indexOf(Math.max(...res))];
}
array = [13 , 2 , 1 , 2 , 10 , 2 , 1 , 1 , 2 , 2];
let frequentElement = mostFrequentElement(array);
console.log(`The frequent element in ${array} is ${frequentElement}`);
Loop on all element and collect the Count of each element in the array that is the idea of the solution
Here is my solution :-
const arr = [
2, 1, 10, 7, 10, 3, 10, 8, 7, 3, 10, 5, 4, 6, 7, 9, 2, 2, 2, 6, 3, 7, 6, 9, 8,
9, 10, 8, 8, 8, 4, 1, 9, 3, 4, 5, 8, 1, 9, 3, 2, 8, 1, 9, 6, 3, 9, 2, 3, 5, 3,
2, 7, 2, 5, 4, 5, 5, 8, 4, 6, 3, 9, 2, 3, 3, 10, 3, 3, 1, 4, 5, 4, 1, 5, 9, 6,
2, 3, 10, 9, 4, 3, 4, 5, 7, 2, 7, 2, 9, 8, 1, 8, 3, 3, 3, 3, 1, 1, 3,
];
function max(arr) {
let newObj = {};
arr.forEach((d, i) => {
if (newObj[d] != undefined) {
++newObj[d];
} else {
newObj[d] = 0;
}
});
let nwres = {};
for (let maxItem in newObj) {
if (newObj[maxItem] == Math.max(...Object.values(newObj))) {
nwres[maxItem] = newObj[maxItem];
}
}
return nwres;
}
console.log(max(arr));
I guess you have two approaches. Both of which have advantages.
Sort then Count or Loop through and use a hash table to do the counting for you.
The hashtable is nice because once you are done processing you also have all the distinct elements. If you had millions of items though, the hash table could end up using a lot of memory if the duplication rate is low. The sort, then count approach would have a much more controllable memory footprint.
var mode = 0;
var c = 0;
var num = new Array();
var value = 0;
var greatest = 0;
var ct = 0;
Note: ct is the length of the array.
function getMode()
{
for (var i = 0; i < ct; i++)
{
value = num[i];
if (i != ct)
{
while (value == num[i + 1])
{
c = c + 1;
i = i + 1;
}
}
if (c > greatest)
{
greatest = c;
mode = value;
}
c = 0;
}
}
You can try this:
// using splice()
// get the element with the highest occurence in an array
function mc(a) {
var us = [], l;
// find all the unique elements in the array
a.forEach(function (v) {
if (us.indexOf(v) === -1) {
us.push(v);
}
});
l = us.length;
while (true) {
for (var i = 0; i < l; i ++) {
if (a.indexOf(us[i]) === -1) {
continue;
} else if (a.indexOf(us[i]) != -1 && a.length > 1) {
// just delete it once at a time
a.splice(a.indexOf(us[i]), 1);
} else {
// default to last one
return a[0];
}
}
}
}
// using string.match method
function su(a) {
var s = a.join(),
uelms = [],
r = {},
l,
i,
m;
a.forEach(function (v) {
if (uelms.indexOf(v) === -1) {
uelms.push(v);
}
});
l = uelms.length;
// use match to calculate occurance times
for (i = 0; i < l; i ++) {
r[uelms[i]] = s.match(new RegExp(uelms[i], 'g')).length;
}
m = uelms[0];
for (var p in r) {
if (r[p] > r[m]) {
m = p;
} else {
continue;
}
}
return m;
}
Let's assume that I have ;
var array = [1,2,3,4,4,5,5];
I want it to be;
var newArray = [1,2,3];
I want to remove the duplicates completely rather than keeping them as unique values. Is there a way achieve that through reduce method ?
You could use Array#filter with Array#indexOf and Array#lastIndexOf and return only the values which share the same index.
var array = [1, 2, 3, 4, 4, 5, 5],
result = array.filter(function (v, _, a) {
return a.indexOf(v) === a.lastIndexOf(v);
});
console.log(result);
Another approach by taking a Map and set the value to false, if a key has been seen before. Then filter the array by taking the value of the map.
var array = [1, 2, 3, 4, 4, 5, 5],
result = array.filter(
Map.prototype.get,
array.reduce((m, v) => m.set(v, !m.has(v)), new Map)
);
console.log(result);
I guess it won't have some remarkable performance, but I like the idea.
var array = [1,2,3,4,4,5,5],
res = array.reduce(function(s,a) {
if (array.filter(v => v !== a).length == array.length-1) {
s.push(a);
}
return s;
}, []);
console.log(res);
Another option is to use an object to track how many times an element is used. This will destroy the array order, but it should be much faster on very large arrays.
function nukeDuplications(arr) {
const hash = {};
arr.forEach(el => {
const qty = hash[el] || 0;
hash[el] = qty+1;
});
const ret = [];
Object.keys(hash).forEach(key => {
if (hash[key] === 1) {
ret.push(Number(key));
}
})
return ret;
}
var array = [1,2,3,4,4,5,5];
console.log(nukeDuplications(array));
A slightly more efficient solution would be to loop over the array 1 time and count the number of occurrences in each value and store them in an object using .reduce() and then loop over the array again with .filter() to only return items that occurred 1 time.
This method will also preserve the order of the array, as it merely uses the object keys as references - it does not iterate over them.
var array = [1,2,3,4,4,5,5];
var valueCounts = array.reduce((result, item) => {
if (!result[item]) {
result[item] = 0;
}
result[item]++;
return result;
}, {});
var unique = array.filter(function (elem) {
return !valueCounts[elem] || valueCounts[elem] <= 1;
});
console.log(unique)
Another option is to use an object to track how many times an element is used. This will destroy the array order, but it should be much faster on very large arrays.
// Both versions destroy array order.
// ES6 version
function nukeDuplications(arr) {
"use strict";
const hash = {};
arr.forEach(el => {
const qty = hash[el] || 0;
hash[el] = qty + 1;
});
const ret = [];
Object.keys(hash).forEach(key => {
if (hash[key] === 1) {
ret.push(Number(key));
}
})
return ret;
}
// ES5 version
function nukeDuplicationsEs5(arr) {
"use strict";
var hash = {};
for (var i = 0; i < arr.length; i++) {
var el = arr[i];
var qty = hash[el] || 0;
hash[el] = qty + 1;
};
var ret = [];
for (let key in hash) {
if (hash.hasOwnProperty(key)) {
if (hash[key] === 1) {
ret.push(Number(key));
}
}
}
return ret;
}
var array = [1, 2, 3, 4, 4, 5, 5];
console.log(nukeDuplications(array));
console.log(nukeDuplicationsEs5(array));
There are a lot of over-complicated, and slow running code here. Here's my solution:
let numbers = [1,2,3,4,4,4,4,5,5]
let filtered = []
numbers.map((n) => {
if(numbers.indexOf(n) === numbers.lastIndexOf(n)) // If only 1 instance of n
filtered.push(n)
})
console.log(filtered)
you can use this function:
function isUniqueInArray(array, value) {
let counter = 0;
for (let index = 0; index < array.length; index++) {
if (array[index] === value) {
counter++;
}
}
if (counter === 0) {
return null;
}
return counter === 1 ? true : false;
}
const array = [1,2,3,4,4,5,5];
let uniqueValues = [];
array.forEach(element => {
if(isUniqueInArray(array ,element)){
uniqueValues.push(element);
}
});
console.log(`the unique values is ${uniqueValues}`);
If its help you, you can install the isUniqueInArray function from my package https://www.npmjs.com/package/jotils or directly from bit https://bit.dev/joshk/jotils/is-unique-in-array.
My answer is used map and filter as below:
x = [1,2,3,4,2,3]
x.map(d => x.filter(i => i == d).length < 2 ? d : null).filter(d => d != null)
// [1, 4]
Object.values is supported since ES2017 (Needless to say - not on IE).
The accumulator is an object for which each key is a value, so duplicates are removed as they override the same key.
However, this solution can be risky with misbehaving values (null, undefined etc.), but maybe useful for real life scenarios.
let NukeDeps = (arr) => {
return Object.values(arr.reduce((curr, i) => {
curr[i] = i;
return curr;
}, {}))
}
I would like to answer my questions with an answer I came up with upon reading it again
const array = [1, 2, 3, 4, 4, 5, 5];
const filtered = array.filter(item => {
const { length } = array.filter(currentItem => currentItem === item)
if (length === 1) {
return true;
}
});
console.log(filtered)
//Try with this code
var arr = [1,2, 3,3,4,5,5,5,6,6];
arr = arr.filter( function( item, index, inputArray ) {
return inputArray.indexOf(item) == index;
});
Also look into this link https://fiddle.jshell.net/5hshjxvr/
I am trying to recursively solve a maze using Javascript, how do I return my solution from my recursive function call?
I am attempting to create a maze solver algorithm using recursion, in Javascript. My maze shall follow the following pattern:
let rawMaze =
[
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
],
Where
0: wall
1: valid path
2: start
3: end
I create an object from the source array,
let maze = []
constructMaze() {
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: uniqueId()
};
this.maze[i].push(Cell);
}
}
console.table(this.maze);
}
I also use a helper function to get the neighbours of any given cell,
getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
The main logic happens in my checkNeighbours function, where I determine the next possible moves and follow them up,
checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
I then proceed to try and put this all together and solve the maze,
initSolve(maze) {
let maze = maze;
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
My question is the following. Using this very contrived and simple maze configuration, I have stepped through the code and can confirm that my
checkNeighbours()
function will recursively arrive at the end. At that point, the function has an array (the variable path) that contains the correct steps through the maze. How do I return this branch, if you will, from the recursive call? What happens when there are multiple branches?
The only thing I can think of is using a global variable, but I feel this can not be correct.
This is ripped from a React frontend , here is runnable code:
let rawMaze = [
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
]
let maze = []
function constructMaze() {
let counter = 0
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: counter
};
maze[i].push(Cell);
counter++
}
}
}
function getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
function checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
function initSolve() {
let maze = constructMaze()
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
Might I recommend adding another class:
function Path() {
this.isValidPath = false;
this.pathArray = [];
}
And also reworking the checkNeighbours function to rename/include these parameters?
checkNeighbours(neighbours, paths, currentPathIndex, visited)
This way, paths could contain an array of Path classes, and you could set the isValidPath flag to true when you found a valid path (assuming you want to also include invalid and valid paths in the array). This would allow you to return all paths (branches). Each branch would be in the paths array at position currentPathIndex, which you'd increment in the code once one path is complete and you want to start searching for another path.
Also, currently the checkNeighbours function appears to do a breadth first search for valid moves. Perhaps if you reworked it into more of a depth-first traversal, then you could add each valid path (and exclude any invalid paths) to the paths array you return.
I have data coming in every second from a Web Socket eg
1- [["X",1],["Y",2],["Z",3]]
2 -[["X",2],["Y",7]]
3 -[["Y",5],["Z",1]]
4 -[["X",7]]
...
The resultant array for each iteration
1 - ["X",1,0],["Y",2,0],["Z",3,0]] // 0 is nothing but the difference it can also be + or minus
2 - ["X",2,1],["Y",7,5],["Z",3,0]] // diff from first iteration
3 - ["X",1,0],["Y",5,-2],["Z",1,-2]] // diff from second
the things i have tried till now
this.socketSubscription = this.socket.messages.subscribe((message) => {
this.prev = this.rows;
this.rows = JSON.parse(message);
if(this.prev){
this.rows.forEach(element => {
for (var index = 0; index < element.length; index++) {
console.log(element[index]);
let check = this.prev.find(prevElement => prevElement.find(el => el[0]));
console.log("check"+check);
/* if (element[0] === ())){
console.log("here");
} */
}
});
}
You don't need the for loop or a nested find. This is close enough and should get you on track:
var results;
function process(data) {
if (results) {
data.forEach(element => {
var key = element[0];
var val = element[1];
var index = results.findIndex(result => result[0] == key);
var prevVal = results[index][1];
var diff = val - prevVal;
results[index][1] = val;
results[index][2] = diff;
});
} else {
results = data.map(element => { element[2] = 0; return element; });
}
}
var a = [["X",1],["Y",2],["Z",3]];
var b = [["X",2],["Y",7]];
var c = [["Y",5],["Z",1]];
process(a); console.log(results); // [["X",1,0], ["Y",2, 0], ["Z",3, 0]]
process(b); console.log(results); // [["X",2,1], ["Y",7, 5], ["Z",3, 0]]
process(c); console.log(results); // [["X",2,0], ["Y",5,-2], ["Z",1,-2]]
Let's assume that I have ;
var array = [1,2,3,4,4,5,5];
I want it to be;
var newArray = [1,2,3];
I want to remove the duplicates completely rather than keeping them as unique values. Is there a way achieve that through reduce method ?
You could use Array#filter with Array#indexOf and Array#lastIndexOf and return only the values which share the same index.
var array = [1, 2, 3, 4, 4, 5, 5],
result = array.filter(function (v, _, a) {
return a.indexOf(v) === a.lastIndexOf(v);
});
console.log(result);
Another approach by taking a Map and set the value to false, if a key has been seen before. Then filter the array by taking the value of the map.
var array = [1, 2, 3, 4, 4, 5, 5],
result = array.filter(
Map.prototype.get,
array.reduce((m, v) => m.set(v, !m.has(v)), new Map)
);
console.log(result);
I guess it won't have some remarkable performance, but I like the idea.
var array = [1,2,3,4,4,5,5],
res = array.reduce(function(s,a) {
if (array.filter(v => v !== a).length == array.length-1) {
s.push(a);
}
return s;
}, []);
console.log(res);
Another option is to use an object to track how many times an element is used. This will destroy the array order, but it should be much faster on very large arrays.
function nukeDuplications(arr) {
const hash = {};
arr.forEach(el => {
const qty = hash[el] || 0;
hash[el] = qty+1;
});
const ret = [];
Object.keys(hash).forEach(key => {
if (hash[key] === 1) {
ret.push(Number(key));
}
})
return ret;
}
var array = [1,2,3,4,4,5,5];
console.log(nukeDuplications(array));
A slightly more efficient solution would be to loop over the array 1 time and count the number of occurrences in each value and store them in an object using .reduce() and then loop over the array again with .filter() to only return items that occurred 1 time.
This method will also preserve the order of the array, as it merely uses the object keys as references - it does not iterate over them.
var array = [1,2,3,4,4,5,5];
var valueCounts = array.reduce((result, item) => {
if (!result[item]) {
result[item] = 0;
}
result[item]++;
return result;
}, {});
var unique = array.filter(function (elem) {
return !valueCounts[elem] || valueCounts[elem] <= 1;
});
console.log(unique)
Another option is to use an object to track how many times an element is used. This will destroy the array order, but it should be much faster on very large arrays.
// Both versions destroy array order.
// ES6 version
function nukeDuplications(arr) {
"use strict";
const hash = {};
arr.forEach(el => {
const qty = hash[el] || 0;
hash[el] = qty + 1;
});
const ret = [];
Object.keys(hash).forEach(key => {
if (hash[key] === 1) {
ret.push(Number(key));
}
})
return ret;
}
// ES5 version
function nukeDuplicationsEs5(arr) {
"use strict";
var hash = {};
for (var i = 0; i < arr.length; i++) {
var el = arr[i];
var qty = hash[el] || 0;
hash[el] = qty + 1;
};
var ret = [];
for (let key in hash) {
if (hash.hasOwnProperty(key)) {
if (hash[key] === 1) {
ret.push(Number(key));
}
}
}
return ret;
}
var array = [1, 2, 3, 4, 4, 5, 5];
console.log(nukeDuplications(array));
console.log(nukeDuplicationsEs5(array));
There are a lot of over-complicated, and slow running code here. Here's my solution:
let numbers = [1,2,3,4,4,4,4,5,5]
let filtered = []
numbers.map((n) => {
if(numbers.indexOf(n) === numbers.lastIndexOf(n)) // If only 1 instance of n
filtered.push(n)
})
console.log(filtered)
you can use this function:
function isUniqueInArray(array, value) {
let counter = 0;
for (let index = 0; index < array.length; index++) {
if (array[index] === value) {
counter++;
}
}
if (counter === 0) {
return null;
}
return counter === 1 ? true : false;
}
const array = [1,2,3,4,4,5,5];
let uniqueValues = [];
array.forEach(element => {
if(isUniqueInArray(array ,element)){
uniqueValues.push(element);
}
});
console.log(`the unique values is ${uniqueValues}`);
If its help you, you can install the isUniqueInArray function from my package https://www.npmjs.com/package/jotils or directly from bit https://bit.dev/joshk/jotils/is-unique-in-array.
My answer is used map and filter as below:
x = [1,2,3,4,2,3]
x.map(d => x.filter(i => i == d).length < 2 ? d : null).filter(d => d != null)
// [1, 4]
Object.values is supported since ES2017 (Needless to say - not on IE).
The accumulator is an object for which each key is a value, so duplicates are removed as they override the same key.
However, this solution can be risky with misbehaving values (null, undefined etc.), but maybe useful for real life scenarios.
let NukeDeps = (arr) => {
return Object.values(arr.reduce((curr, i) => {
curr[i] = i;
return curr;
}, {}))
}
I would like to answer my questions with an answer I came up with upon reading it again
const array = [1, 2, 3, 4, 4, 5, 5];
const filtered = array.filter(item => {
const { length } = array.filter(currentItem => currentItem === item)
if (length === 1) {
return true;
}
});
console.log(filtered)
//Try with this code
var arr = [1,2, 3,3,4,5,5,5,6,6];
arr = arr.filter( function( item, index, inputArray ) {
return inputArray.indexOf(item) == index;
});
Also look into this link https://fiddle.jshell.net/5hshjxvr/