Related
I want to count how often a number in an Array occurs. For example, in Python I can use Collections.Counter to create a dictionary of how frequently an item occurs in a list.
This is as far as I've gotten in JavaScript:
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
/* obj[array[i]] = +=1 */ <= pseudo code
}
How can I create this frequency counter object?
Close but you can't increment undefined so you need to set initial value if it doesn't exist
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
obj[array[i]] = (obj[array[i]] || 0) +1 ;
}
You were almost there. See below code:
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
obj[array[i]] = (obj[array[i]] || 0 ) +1;
}
console.log(obj);
Create an object and check if that specific key exist.If exist then increase it's value by 1
var array = [1, 4, 4, 5, 5, 7];
var obj = {};
for (var i = 0; i < array.length; i++) {
if (obj.hasOwnProperty(array[i])) {
obj[array[i]] += 1;
} else {
obj[array[i]] = 1;
}
}
console.log(obj)
You can use the ? : ternary operator to set initial value as 1 and then increment it on subsequent matches.
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
obj[array[i]] = obj[array[i]]?obj[array[i]]+1:1;
}
console.log(obj);
If the array is always going to be same, and you are going to check frequency of multiple items in the same array without it it being modified, #JohanP's answer is good.
But if you are only going to check frequency of only one item, or the array can change, creating the object is nothing but extra overhead.
In that case, you can do something like this:
const getItemFrequency = function(array, item) {
return array.filter(i => i === item).length;
}
var array = [1,4,4,5,5,7];
console.log(getItemFrequency(array, 4));
Concise logic written as proper function:
function countArrayItemFrequecy(array) {
const length = array.length;
const map = {};
for ( let i = 0; i < length; i++ ) {
let currentItem = array[i];
if (typeof map[currentItem] !== 'undefined' ) {
map[currentItem]++
} else {
map[currentItem] = 1
}
}
return map;
}
You need to make sure to assign default value to your frequency object for the first occurrence of the item. As a shortcut you can use ternary operator
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
obj[array[i]] = obj[array[i]] ? obj[array[i]]++ : 1;
}
which is the same as:
var array = [1,4,4,5,5,7];
var obj = {};
for (var i=0; i < array.length; i++) {
if (obj[array[i]]) {
obj[array[i]]++;
} else {
obj[array[i]] = 1;
}
}
You can use Object.assign: below clones map and then increments/adds the counter. These are pure (no side effects/param reassignment), single-purpose functions.
addToMap does the same thing as { ...map, map[e]: [e]: (map[e] || 0) + 1 }, but that requires babel.
const addToMap = (map, e) => Object.assign({}, map, { [e]: (map[e] || 0) + 1 });
const buildMap = a => a.reduce(addToMap, {});
Using Array.reduce:
arr.reduce(function (acc, item) {
acc[item] = (acc[item] || 0) + 1;
return acc;
}, {});
Example:
var arr = [1,1,2,4,1,4];
var counts = arr.reduce(function (acc, item) {
acc[item] = (acc[item] || 0) + 1;
return acc;
}, {});
console.log(counts);
I want to remove the third occurrence of a character from a string.
Below is what I tried from my end:
function unique(list) {
var result = [];
function findOccurrences(arr, val) {
var i, j,
count = 0;
for (i = 0, j = arr.length; i < j; i++) {
(arr[i] === val) && count++;
}
return count;
}
$.each(list, function(i, e) {
if (findOccurrences(list, e) < 3) result.push(e);
});
return result.join("");
}
var srting = "DGHKHHNL";
var thelist = srting.split("")
console.log(unique(thelist));
Here are some expected results:
Input: DGHKHHNL
Expected: DGHKHNL
Input: AFKLABAYBIB
Expected: AFKLABYBI
Input: JNNNKNND
Expected: JNNKD
https://regex101.com/r/WmUPWW/1 .. I tried using this regex as well to solve the issue. But this this doesn't solves the issue as well.
Please help
Instead of counting the occurrences, you should check the occurrence count for the specific index you are evaluating. Basically, if it's the 3rd or more time that it has appeared, then you don't want it.
A slight change to your code can achieve this (you may want to choose a better function name):
function unique(list) {
var result = [];
function findOccurrenceIndex(arr, val, index) {
var i, j,
count = 0;
for (i = 0, j = arr.length; i < j; i++) {
(arr[i] === val) && count++;
if (i == index) {
return count;
}
}
return count;
}
$.each(list, function(i, e) {
if (findOccurrenceIndex(list, e, i) < 3) result.push(e);
});
return result.join("");
}
var srting = "DGHKHHNL";
var thelist = srting.split("")
console.log(unique(thelist));
Here is a working example
Note that this answer is based on your current code, I expect you could refactor the logic to reduce the code clutter.
In fact, the following reduces the code to a single loop. It works by building a dictionary of character counts as it works though the list. (It also doesn't rely on JQuery like your original attempt):
function unique(list) {
var result = [];
var counts = {};
for (var i = 0; i < list.length; i++) {
var c = list[i];
if (!counts[c])
counts[c] = 0;
counts[c]++;
if (counts[c] < 3) {
result.push(c);
}
}
return result.join("");
}
Here is a working example
An alternate approach which doesn't rely on jQuery (although you could easily swap that with a forEach):
function unique(str) {
var count = {}
return str.split("").reduce((acc, cur) => {
if (!(cur in count)) {
count[cur] = 1;
acc.push(cur);
return acc;
}
if (count[cur] == 2) return acc;
acc.push(cur);
count[cur]++;
return acc;
}, []).join("");
}
Here I used two helper array result and tempCount . tempCount is store each alphabet as key and count it ,so if it is exceed more than 3
function unique(list) {
var result = [];
var tempCount = [];
list = list.split("");
for(var i=0;i < list.length;i++) {
if(tempCount[list[i]]) {
if(tempCount[list[i]] == 2) continue;
tempCount[list[i]]++;
} else {
tempCount[list[i]] = 1;
}
result.push(list[i]);
}
return result.join("");
}
var srting = "JNNNKNND";
console.log(unique(srting));
Building off the answer by #musefan, another ES6 approach can use Array.reduce to build the counts/output based on an accumulator object:
const onlyTwo = list => list.split('').reduce((cache, letter) => {
cache[letter] ? cache[letter]++ : cache[letter] = 1;
if (cache[letter] < 3) cache.output += letter;
return cache;
}, {
output: ''
}).output;
console.log(onlyTwo('DGHKHHNL'));
console.log(onlyTwo('AFKLABAYBIB'));
console.log(onlyTwo('JNNNKNND'));
You can improve this by applying functional programming principles to separate the concerns of counting the duplicates and generating the output string. This way you can utilize the same accumulation technique with different max values.
const maxDuplicates = max => list => list.split('').reduce((cache, letter) => {
cache[letter] ? cache[letter]++ : cache[letter] = 1;
if (cache[letter] <= max) cache.output += letter;
return cache;
}, {
output: ''
}).output;
const onlyTwo = maxDuplicates(2);
console.log(onlyTwo('DGHKHHNL'));
console.log(onlyTwo('AFKLABAYBIB'));
console.log(onlyTwo('JNNNKNND'));
const onlyOne = maxDuplicates(1);
console.log(onlyOne('DGHKHHNL'));
console.log(onlyOne('AFKLABAYBIB'));
console.log(onlyOne('JNNNKNND'));
As the title, if the input is [[1,2], [3,4], [1,3], [5,6], [6,5]], output should be [[1,2,3,4], [5,6]].
It's wrong on the recursive part. In my code, after running it, I will get [[1,2,3],[1,3,4],[5,6]], which means I need once more merge, but I'm confused how to continue the code until no sub-array contains common element.
Here is my code
function need_merge_or_not(arr)
{
for (var i = 0; i <= arr.length-1; i++) {
for (var j = i+1; j <= arr.length-1; j++) {
var arr_new = arr[i].concat(arr[j]);
//remove deplicates
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
return true;
}
}
}
return false;
}
function merge(arr)
{
if (arr.length >= 2) {
for (var i = 0; i <= arr.length-1; i++) {
for (var j = i+1; j <= arr.length-1; j++) {
var arr_new = arr[i].concat(arr[j]);
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
arr.splice(arr.indexOf(arr[i]), 1);
arr.splice(arr.indexOf(arr[j]),1);
arr.push(arr_merge);
}
}
if (need_merge_or_not(arr)) {
return merge(arr);
}
}
}
return arr;
}
I figured it out. Here is the code:
function merge(arr){
var input = [];
for(var i = 0; i < arr.length; i++){
input.push(arr[i]);
}
if (arr.length >= 2) {
for (var i = 0; i < arr.length; i++) {
for (var j = i+1; j < arr.length; j++) {
var arr_new = arr[i].concat(arr[j]);
//remove duplicates
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
arr.splice(arr.indexOf(arr[i]), 1, arr_merge);
arr.splice(arr.indexOf(arr[j]),1);
j--;
}
}
}
if (!arraysEqual(input, arr)) {merge(arr)};
}
return arr;
//Input:[[1,2], [3,4], [1,3], [5,6], [6,5]]
//Output:[[1,2,3,4], [5,6]]
}
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
You could use two hash tables, one for the items and their groups and on for the result sets.
Basically the algorithm generates for the same group an object with a property and an array, because it allowes to keep the object reference while assigning a new array.
The main part is iterating the outer array and then the inner arrays and check inside, if it is the first item, then check the hash table for existence and if not exists, generate a new object with a values property and an empty array as value. Also assign the actual object to sets with item as key.
In a next step, the hash table is checked again and if not exist, then assign the object of the first element.
To maintain only unique values, a check is made and if the item does not exist, the item is pushed to the hash table's values array.
Then a part to join arrays follows by checking if the object of the first item is not equal to object of the actual item. If so, it delete from sets the key from the actual items's values first item and concat the array of the actual items to the first item's object's values. Then the values object is assigned to the actual item's object.
Later the sets are maped to the result set with iterating the sets object and the values property is taken as value.
var array = [[1, 2], [3, 4], [1, 3], [5, 6], [6, 5]],
groups = {},
sets = {},
result;
array.forEach(function (a) {
a.forEach(function (b, i, bb) {
if (i === 0 && !groups[b]) {
groups[b] = { values: [] };
sets[b] = groups[b];
}
if (!groups[b]) {
groups[b] = groups[bb[0]];
}
if (groups[b].values.indexOf(b) === -1) {
groups[b].values.push(b);
}
if (groups[bb[0]] !== groups[b]) {
delete sets[groups[b].values[0]];
groups[bb[0]].values = groups[bb[0]].values.concat(groups[b].values);
groups[b].values = groups[bb[0]].values;
}
});
});
result = Object.keys(sets).map(function (k) {
return sets[k].values;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
that is found here in stack but i want somes changes.
function perms(data) {
if (!(data instanceof Array)) {
throw new TypeError("input data must be an Array");
}
data = data.slice(); // make a copy
var permutations = [],
stack = [];
function doPerm() {
if (data.length == 0) {
permutations.push(stack.slice());
}
for (var i = 0; i < data.length; i++) {
var x = data.splice(i,1);
stack.push(x);
doPerm();
stack.pop();
data.splice(i, 0, x);
}
}
doPerm();
return permutations;
}
var input = "552".split('');
var result = perms(input);
for (var i = 0; i < result.length; i++) {
result[i] = result[i].join('-');
}
The result of that is :
5-5-2
5-2-5
5-5-2
5-2-5
2-5-5
2-5-5
but , are 3 elements duplicates the result must be :
5-5-2
5-2-5
2-5-5
how can i fix that issue .
Basically, you have one issue,
var x = data.splice(i, 1)[0];
// ^^^ is missing
because you get an array with splicing. The result is a deep nested array with
data.splice(i, 0, x);
This inserts the array later on position i.
For preventing duplicates, you need a check, if the actual value is already inserted in the result set with
permutations.some(function (a) {
return a.every(function (b, j) {
return stack[j] === b;
});
}) || permutations.push(stack.slice());
which test the arrays and if no match, the push is performed.
function perms(data) {
if (!(data instanceof Array)) {
throw new TypeError("input data must be an Array");
}
data = data.slice(); // make a copy
var permutations = [],
stack = [],
hash = Object.create(null);
function doPerm() {
if (data.length == 0) {
permutations.some(function (a) {
return a.every(function (b, j) {
return stack[j] === b;
});
}) || permutations.push(stack.slice());
return;
}
for (var i = 0; i < data.length; i++) {
var x = data.splice(i, 1)[0];
stack.push(x);
doPerm();
stack.pop();
data.splice(i, 0, x);
}
}
doPerm();
return permutations;
}
var input = "552".split('');
var result = perms(input);
for (var i = 0; i < result.length; i++) {
result[i] = result[i].join('-');
}
console.log(result);
Check if array array is present within resulting array before calling .push() using Array.prototype.some(), Array.prototype.join()
function p(a, b, res) {
var b = b || [],
res = res || [],
len = a.length;
if (!len) {
// check if `res` contains `b.join("")`
if (!res.length
|| !res.some(function(n) {
return n.join("") === b.join("")
}))
res.push(b)
} else {
for (var i = 0
; i < len; p(a.slice(0, i).concat(a.slice(i + 1, len))
, b.concat(a[i]), res)
, i++);
}
return res
}
var result = p("552".split(""));
result = result.map(function(res) {
return res.join("-")
});
console.log(result);
I have an javascript array and I want to delete an element based on the value of the array, this is my array and this is what I have tried without success.
array = []
array.push (["Mozilla","Firefox",1.10])
index = array.indexOf(["Mozilla","Firefox",1.10])
array.splice(index, 1)
But it doesn't work, any idea¿?
You're trying to compare arrays, which are objects and have unique addresses. Your index variable is -1.
Try ['Mozilla','Firefox',1.10] === ['Mozilla','Firefox',1.10] in your console, you'll see that just because two arrays have the same values, it doesn't mean they are the same array.
What you need is a deep-equals style of comparison, that checks each value in the array, to see if two arrays have a likeness.
Take a look at lodash's isEqual function for an idea.
Here's a simple looping function:
function deepIndex(array, comparison) {
var i, j;
main:
for (i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
for (j = 0; j < array[i].length; j++) {
if (array[i][j] !== comparison[j]) {
continue main;
}
}
return i;
}
}
}
var arr = [];
arr.push('string', ['Mozilla','Firefox',1.10], 'thing');
var index = deepIndex(arr, ['Mozilla','Firefox',1.10])
console.log(index, arr);
arr.splice(index, 1);
console.log(arr);
Take a look at this:
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
This is function, made by the Creator of JQUery.
Basically you take the Index of one thing and than it is getting removed
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
//Equals Function taken from:
//http://stackoverflow.com/questions/7837456/comparing-two-arrays-in-javascript
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
array = [];
array.push (["Mozilla","Firefox",1.10]);
array.push (["Microsoft","Spartan",1.0]);
array.push (["Safari","Safari",1.4]);
index = indexOfArr(array,["Mozilla","Firefox",1.10]);
array.remove(index, index);
document.getElementById("length").innerHTML = array.length;
for(var i = 0; i < array.length; i++){
document.getElementById("elems").innerHTML += "<br>"+array[i];
}
function indexOfArr(hay, needle){
for(var i = 0; i < hay.length; i++){
if (hay[i].equals(needle)){
return i;
}
}
return -1;
}
<span id = "length"></span><br>
<span id = "elems">Elements:</span>
You can use the fiter metodh, instead of indexOf.
Within the callback of that method, you can choose different approaches:
Use toString on the arrays and compare the two strings
Test for the length and the content, by iterating over the contained elements
... Continue ...
In any case using === will solve the problem, unless the object contained is exactly the same against which you are trying to match.
By the same, I mean the same. We are non speaking about having the same content, but to be the same instance.
Loop over your array and check the equality:
array = [];
array.push(["Mozilla", "Firefox", 1.10]);
for (var i = 0; i < array.length; i++) {
if (arraysEqual(array[i], ["Mozilla", "Firefox", 1.10])) {
array.splice(i, 1);
}
}
function arraysEqual(a, b) {
if (a === b) return true;
if (a === null || b === null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
JSFiddle: http://jsfiddle.net/ghorg12110/r67jts35/
Based on this question : How to check if two arrays are equal with JavaScript?
You can do something like this
array = []
array.push (["Mozilla","Firefox",1.10])
tempArray = array[0];
index = tempArray.indexOf("Mozilla","Firefox",1.10)
array.splice(index, 1)
You can build on this if you put for loop instead of hard coding.