Group array by nested dependent array element - javascript
I have an array store Task information. Each task has also an array of taskId that it is depending on.
Input
let inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
The expected output is grouping all the depending task into one array for displaying into the UI.
Output
[
[
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
}
],
[
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
]
I have made a function to do it but seem I was thinking wrong by hard-coded it. Hope someone can help me archive it right way using pure JavaScript/TypeScript or Underscore since we have already used in the project.
Noted: TaskId will be random string like "5878465507b36e1f9c4c46fe"
// will contain the groups (results).
var result = [];
// will serve as a holder of the already treated indexes of inputArr.
var indexCache = [];
// insert obj into a group, insert its dependencies and the object that depend on it as well.
function insertWithDependencies(obj, group){
// insert this obj into this group
group.push(obj);
// First: look for the objects it depends on
obj.dependOnTasks.forEach(function(id){
for(var i = 0; i < inputArr.length; i++){
// if the object in this index is already treated, then ignore it
if(indexCache.indexOf(i) != -1) continue;
// if this object is a dependency of obj then insert it with its own dependencies.
if(inputArr[i].id == id){
var o = inputArr[i];
indexCache.push(i); // cache this i as well
insertWithDependencies(o, group);
}
}
});
// Then: look for the objects that depends on it
for(var i = 0; i < inputArr.length; i++){
// if the object in this index is already treated, then ignore it
if(indexCache.indexOf(i) != -1) continue;
// if this object depends on obj then insert it with ...
if(inputArr[i].dependOnTasks.indexOf(obj.id) != -1){
var o = inputArr[i];
indexCache.push(i); // cache i
insertWithDependencies(o, group);
}
}
};
// while there is element in the inputArr that haven't been treated yet
while(inputArr.length != indexCache.length){
// the group that will hold the depending tasks all together
var group = [];
// look for the first untreated object in inputArr
var i;
for(i = 0; i < inputArr.length; i++)
if(indexCache.indexOf(i) == -1)
break;
var obj = inputArr[i];
// cache its index
indexCache.push(i)
// insert it along its dependencies
insertWithDependencies(obj, group);
// push the group into the result array
result.push(group);
}
ANOTHER WAY:
Here is an optimised way to do it, but the data inside inputArr will be lost afterwards. It won't use the indexCache to see if an index is already treated or not but instead it will make all treated items null in inputArr. So if you don't care or won't use inputArr afterwards, use this instead:
var result = [];
function insertWithDependencies(obj, group){
group.push(obj);
obj.dependOnTasks.forEach(function(id){
for(var i = 0; i < inputArr.length; i++){
if(!inputArr[i]) continue;
if(inputArr[i].id == id){
var o = inputArr[i];
inputArr[i] = null;
insertWithDependencies(o, group);
}
}
});
for(var i = 0; i < inputArr.length; i++){
if(!inputArr[i]) continue;
if(inputArr[i].dependOnTasks.indexOf(obj.id) != -1){
var o = inputArr[i];
inputArr[i] = null;
insertWithDependencies(o, group);
}
}
};
function findNotNull(){
for(var i = 0; i < inputArr.length; i++)
if(inputArr[i]) return i;
return -1;
}
var index;
while((index = findNotNull()) != -1){
var group = [];
var obj = inputArr[index];
inputArr[index] = null;
insertWithDependencies(obj, group);
result.push(group);
}
console.log(result);
The solution is straightforward,
Initialize empty group list
For each task find if there is a group in the group list with id or id of any dependent tasks
If not add a new group with task id and dependent tasks
var input = [
{ id: 1, dependOnTasks: [2, 3] },
{ id: 2, dependOnTasks: [3] },
{ id: 3, dependOnTasks: [] },
{ id: 4, dependOnTasks: [5] },
{ id: 5, dependOnTasks: [] },
{ id: 6, dependOnTasks: [5] }
];
var groups = [];
for (var i = 0; i < input.length; i++){
var group = findGroup(groups,input[i]);
if (!group){
group = {ids : []};
group.ids.push(input[i].id);
groups.push(group);
}
if (group.ids.indexOf(input[i].id) === -1){
group.ids.push(input[i].id);
}
for (var j = 0; j < input[i].dependOnTasks.length; j++){
if (group.ids.indexOf(input[i].dependOnTasks[j]) === -1){
group.ids.push(input[i].dependOnTasks[j]);
}
}
}
document.write(groups[0].ids + '</br>');
document.write(groups[1].ids + '</br>');
function findGroup(groups,task){
for (var i = 0; i < groups.length; i++){
var group = groups[i];
if (group.ids.indexOf(task.id) !== -1){
return group;
}
for (var j = 0; j < task.dependOnTasks.length; j++){
if (group.ids.indexOf(task.dependOnTasks[j]) !== -1){
return group;
}
}
}
return null;
}
If you don't care about the order of the tasks in the same group. Using union and find to implement a disjoint set might be an option.
Util data structure:
function UnionFind(n) {
this.parent = [...Array(n+1).keys()]
}
UnionFind.prototype.find = function(x) {
if (this.parent[x] === x) {
return x
}
const ret = this.find(this.parent[x])
this.parent[x] = ret
return ret
}
UnionFind.prototype.union = function(x, y) {
let x_rep = this.find(x)
let y_rep = this.find(y)
if (x_rep !== y_rep) {
this.parent[x_rep] = y_rep
}
}
Dumb data source:
let inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
Driver program:
let len = inputArr.length
let uf = new UnionFind(len)
// iterate through all tasks to group them
inputArr.forEach(entry => {
entry.dependOnTasks.forEach(depsId => {
uf.union(entry.id, depsId)
})
})
// reiterate to retrieve each task's group and group them using a hash table
let groups = {}
inputArr.forEach(entry => {
const groupId = uf.find(entry.id)
if (!groups.hasOwnProperty(groupId)) {
groups[groupId] = [entry]
return
}
groups[groupId].push(entry)
})
let result = Object.keys(groups).map(groupId => groups[groupId])
console.log(JSON.stringify(result, null, 2))
note: if id is random string in your case, simply change this.parent to hash map, and if you care about the order(as there're dependency trees), consider using topological sort instead.
You can try with my code
var inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
// make matrix graph
var map = {};
for (var i = 0; i < inputArr.length; i++) {
var task = inputArr[i];
map[task.id] = map[task.id] || {};
for (var j = 0; j < task.dependOnTasks.length; j++) {
var dependId = task.dependOnTasks[j];
map[dependId] = map[dependId] || {};
map[task.id][dependId] = true;
map[dependId][task.id] = true;
}
}
var groupTasks = [];
for (var key in map) {
var group = groupTasks.filter(function(e) {
return e.indexOf(key) >= 0;
})[0]
if (!group) {
group = [key];
groupTasks.push(group);
}
for (var dependKey in map[key]) {
if (group.indexOf(dependKey) == -1) {
group.push(dependKey);
}
}
}
var result = groupTasks.map(function(group) {
var tasks = [];
group.forEach(function(id) {
var task = inputArr.filter(function(e) { return e.id == id })[0];
tasks.push(task);
});
return tasks;
})
console.log(JSON.stringify(result, null, 4));
Related
Find combination of nested JSON keys
I am having some trouble solving this issue without using nested for-loops. I would like to do it using recursion. Given the following object: { "Color": { "Black": [], "White": [] }, "Effect": { "30W": [], "40W": [], "60W": [] } } The code should compute each combination of Color and Effect and add a number in the list such that the following is produced: { "Color": { "Black": [ 1, 2, 3 ], "White": [ 4, 5, 6 ] }, "Effect": { "30W": [ 1, 4 ], "40W": [ 2, 5 ], "60W": [ 3, 6 ] } } My attempt is as follows: const func = (object, entries) => { for (let prop in object) { let counter = 0; const objLength = Object.keys(object[prop]).length; for (let key in object[prop]) { console.log(key + counter) for (let i = 0; i < entries.length / objLength; i++) { object[prop][key].push(entries[counter]); counter++; } } } return object; } However, this does not return the desired output. I think it is because of the inner-most for loop condition.
The best way to handle this is to create your JavaScript object and convert it to a string. // creating your object with attributes. Objects in objects or whatever you // need var obj = new Object(); obj.name = "Dale"; obj.age = 30; obj.married = true; dataToAdd.forEach(function(item, index) { item.married = false; }) // Then convert it to a string using the following code var jsonString= JSON.stringify(obj); console.log(jsonString);
I solved the question by considering the space of each attribute key. Then it is just a matter of finding the cartesian, and adding values accordingly: const cartesian =(...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat()))); function diy(jsonObj, counter) { let permObj = [] let keys = Object.keys(jsonObj) keys.forEach(key => { permObj.push(Object.keys(jsonObj[key])) }); permObj = cartesian(...permObj) for(let i = 0; i < keys.length; i++) { for(let j = 0; j < permObj.length; j++) { jsonObj[keys[i]][permObj[j][i]].push(j + counter); } } return jsonObj; }
Javascript: object array mapping and matching with IE11
I'm looking for a javascript implementation (for IE11) for this problem; my inputs are two arrays like these: var array1 = [{id: 1, param:"bon jour"}, {id: 2, param:"Hi"}, {id: 3, param:"Hello"}]; var array2 = [{item: "Peter", values:"1,2", singlevalue:"2"}, {item: "Mark", values:"1,2,3", singlevalue:"3"}, {item: "Lou", values:"2", singlevalue:"2"}]; and I should create a new array (array3) with array2 data plus 2 new fields ("params" and "singleparam"), using matching between array1[i].id and array2[x].values to evaluate "params" and between array1[i].id and array2[x].singlevalue to evaluate "singleparam", with this kind of result: array3 = [{item: "Peter", values:"1,2", singlevalue:"2", params:"bon jour,Hi", singleparam:"Hi"}, {item: "Mark", values:"1,2,3", singlevalue:"3", params:"bon jour,Hi,Hello", singleparam:"Hello"}, {item: "Lou", values:"2", singlevalue:"2", params:"Hi", singleparam:"Hi"}]; I'm a javascript newbie and I've tried this kind of solution: var array3 = array2.map(function(x, array1) { const newOb = {}; newOb.item = x.item; newOb.values = x.values; newOb.singlevalue = x.singlevalue; newOb.params = function(x.values, array1) { var str = ""; var idArray = x.values.split(","); for(i = 0; i < idArray.lenght; i++) { for(j = 0; i < array1.lenght; j++) { if(idArray[i] == array1[j].id) { str += array1[j].param + ","; break; } } } return str; }; newOb.singleparam = function(x.singlevalue, array1) { var val; for(j = 0; i < array1.lenght; j++) { if(array1[j].id == x.singlevalue) val = array1[j].param; } return val; } return newOb; }); console.log(array3); with this error: Error: Unexpected token '.' I'd like to find an efficient solution considering that array1 has less than 10 elements, but array2 could contains more than 1000 objects. Thanks in advance for your support
I will skip the functions stop and singlevalues and there were also some syntax errors, for example the correct one is length and not lenght var array1 = [{id: 1, param:"bon jour"}, {id: 2, param:"Hi"}, {id: 3, param:"Hello"}]; var array2 = [{item: "Peter", values:"1,2", singlevalue:"2"}, {item: "Mark", values:"1,2,3", singlevalue:"3"}, {item: "Lou", values:"2", singlevalue:"2"}]; function newArray3() { return array2.map(x => { const newOb = {}; newOb.item = x.item; newOb.values = x.values; newOb.singlevalue = x.singlevalue; newOb.params = paramsFunction(x.values, array1); newOb.singleparam = singleParamFunction(x.singlevalue, array1); return newOb; }) } function singleParamFunction(x, array1) { var val; for(i = 0; i < array1.length; i++) { if(array1[i].id.toString() == x) { val = array1[i].param; } } return val; } function paramsFunction(x, array1) { var str = ""; var idArray = x.split(","); for(i = 0; i < idArray.length; i++) { for(j = 0; j < array1.length; j++) { if(idArray[i] == array1[j].id.toString()) { str += array1[j].param + ","; break; } } } return str; } array3 = newArray3(); console.log(array3)
The solution provided by the #Walteann Costa can show the desired results in other browsers but it will not work for the IE browser as his code sample uses the => Arrow functions that is not supported in the IE browser. As your question asks the solution for the IE browser, I tried to modify the code sample provided by the #Walteann Costa. Below modified code can work with the IE 11 browser. <!doctype html> <html> <head> <script> "use strict"; var array1 = [{ id: 1, param: "bon jour" }, { id: 2, param: "Hi" }, { id: 3, param: "Hello" }]; var array2 = [{ item: "Peter", values: "1,2", singlevalue: "2" }, { item: "Mark", values: "1,2,3", singlevalue: "3" }, { item: "Lou", values: "2", singlevalue: "2" }]; function newArray3() { return array2.map(function (x) { var newOb = {}; newOb.item = x.item; newOb.values = x.values; newOb.singlevalue = x.singlevalue; newOb.params = paramsFunction(x.values, array1); newOb.singleparam = singleParamFunction(x.singlevalue, array1); return newOb; }); } function singleParamFunction(x, array1) { var val,i,j; for (i = 0; i < array1.length; i++) { if (array1[i].id.toString() == x) { val = array1[i].param; } } return val; } function paramsFunction(x, array1) { var str = ""; var idArray = x.split(","); var i,j; for (i = 0; i < idArray.length; i++) { for (j = 0; j < array1.length; j++) { if (idArray[i] == array1[j].id.toString()) { str += array1[j].param + ","; break; } } } return str; } var array3 = newArray3(); console.log(array3[0]); console.log(array3[1]); console.log(array3[2]); </script> </head> <body> </body> </html> Output in the IE 11:
from an array of objects how do I find which value comes up most often, in javascript? [duplicate]
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; }
javascript leave max 3 occurrences of the same obj in an array
If I populate an array of pairs (inside a get json) like so: var arrItems = []; for (let y=0; y<50 ;y++){ var titl = data.item[y].name; var img = data.item[y].image[0].url; arrItems.push({t:titl,i:img}); } How can I then filter it to leave only 3 pairs where value is the same? Example: arrItems = [ {t:one,i:square.jpg}, {t:two,i:square.jpg}, {t:three,i:square.jpg}, {t:four,i:square.jpg}, {t:five,i:triangle.jpg} ]; Becomes arrItems = [ {t:one,i:square.jpg}, {t:two,i:square.jpg}, {t:three,i:square.jpg}, {t:five,i:triangle.jpg} ]; Both JavaScript or jQuery are OK.
You could take a hash table and count the occurences of the wanted property and filter with a max value. var items = [{ t: 'one', i: 'square.jpg' }, { t: 'two', i: 'square.jpg' }, { t: 'three', i: 'square.jpg' }, { t: 'four', i: 'square.jpg' }, { t: 'five', i: 'triangle.jpg' }], count = {}, result = items.filter(({ i }) => { count[i] = (count[i] || 0) + 1; return count[i] <= 3; }); console.log(result); .as-console-wrapper { max-height: 100% !important; top: 0; }
There might be some more efficient ways to write it, but I would think the easiest to understand is the straightforward iteration with counting: var counts = {}; for (var i = 0; i < arrItems.length; i++) { var item = arrItems[i]; var count = counts[item.i] || 0; if (count >= 3) { arrItems.splice(i, 1); i--; } else { counts[item.i] = ++count; } }
You can use reduce function var arrItems = [ {t:'one',i:'square.jpg'}, {t:'two',i:'square.jpg'}, {t:'three',i:'square.jpg'}, {t:'four',i:'square.jpg'}, {t:'five',i:'triangle.jpg'} ]; var output = arrItems.reduce((acc, {t,i})=>{ acc['mem'][i] = (acc['mem'][i] || 0) + 1; acc['mem'][i] <= 3 ? acc['output'].push({t, i}) : ''; return acc; }, {'mem':{}, 'output':[]}); console.log(output);
Need an algorithm to manipulate array structure in javascript
In javascript, here is my start array: [{ name: 'aaa', value: 1 }, { name: 'bbb', value: 0 }, { name: 'bbb', value: 1 }] I want to transform it into this array as result: [{ name: 'aaa', value: 1 }, { name: 'bbb', value: [0, 1] }] I need a good and simple algorithm to do this
How about: var array = [{ name: 'aaa', value: 1 }, { name: 'bbb', value: 0 }, { name: 'bbb', value: 1 }]; var map = {}; for(var i = 0; i < array.length; i++) { var name = array[i].name; if (map[name] === undefined) { map[name] = []; } map[name].push(array[i].value); } var result = []; for(var key in map) { var value = map[key]; result.push({ name: key, value: value.length === 1 ? value[0] : value }); } Easiest way is to create a map to keep track of which names are used. Then convert this map back to an array of objects. If you want to use Arrays for value then change it to: result.push({ name: key, value: value });
here's pseudocode for simplest implementation hash = {} for(pair in array) { hash[pair.name] ||= [] hash[pair.name] << pair.value } result = [] for(k, v in hash) { result << {name: k, value: v} }
This function does the trick function consolidate(var arrayOfObjects) { // create a dictionary of values first var dict = {}; for(var i = 0; i < arrayOfObjects.length; i++) { var n = arrayOfObjects[i].name; if (!dict[n]) { dict[n] = []; } dict[n].push(arrayOfObjects[i].value); } // convert dictionary to array again var result = []; for(var key in dict) { result.push({ name: key, value: dict[key].length == 1 ? dict[key][0] : dict[key] }); } return result; }
An alternative solution: function convert(arr) { var res = []; var map = {}; for (var i=0;i<arr.length;i++) { var arrObj = arr[i]; var oldObj = map[arrObj.name]; if (oldObj == undefined) { oldObj = {name:arrObj.name, value:arrObj.value}; map[arrObj.name] = oldObj; res.push(oldObj); } else { if( typeof oldObj.value === 'number' ) { oldObj.value = [oldObj.value]; } oldObj.value.push(arrObj.value); } } return res; } In theory it should work a bit faster and use less memory. Basically it creates a result array and a map which is an index for the same array (no duplicate objects). So it fills the result in one iteration instead of two and does not need to convert map to array (which saves several CPU cycles :P ). Added: Here is a variation of that function in case value: [1] is acceptable: function convert(arr) { var res = []; var map = {}; for (var i=0;i<arr.length;i++) { var arrObj = arr[i]; var oldObj = map[arrObj.name]; if (oldObj == undefined) { oldObj = {name:arrObj.name, value:[arrObj.value]}; map[arrObj.name] = oldObj; res.push(oldObj); } else { oldObj.value.push(arrObj.value); } } return res; }