Counter inkrementing - javascript

so I just learn how to code in JS, so i just want to warn you that my code may looks very awful for you.
I want a "class" that counts how often a number has been counted.
Example:
counter = new Counter();
counter.count(1);
counter.count(1);
counter.count(3);
Calling the getCounts gives the result:
counter.getCounts();
> 1: 2, 3: 1
My code works, but i have two problems.
Since I store it in an array, I can output the numbers not in order, but in the order they were called.
I don't think it's a pretty solution.
My code:
class Counter {
constructor(arr = []) {
this.arr = arr;
}
count(number) {
var posNumber = [];
var evenNumCheck = false;
var exactPos;
//Check if have the number already in the array
for (var i = 0; i < this.arr.length; i++) {
if (this.arr[i] === number) {
posNumber.push(i);
}
}
//posNumber we have the position of all the numbers that match our argument number
// we check which of them has an even position, only this can be the number we want
// since we save [number,count; number,count]
for (var i = 0; i < posNumber.length; i++) {
if (i % 2 === 0) {
evenNumCheck = true;
exactPos = i;
}
}
if (evenNumCheck) {
this.arr[exactPos + 1]++;
} else {
this.arr.push(number);
this.arr.push(1);
}
}
getCounts() {
var string = '';
for (var i = 0; i < this.arr.length; i += 2) {
if (i + 2 >= this.arr.length) {
string += this.arr[i] + ': ' + this.arr[i + 1];
} else {
string += this.arr[i] + ': ' + this.arr[i + 1] + ', ';
}
}
console.log(string);
}
}
Do you think this solution is ok or are there better solutions for it, which might also output the numbers in order?

The classic way is to use an object as said by #Orkhan Alikhanov
class Counter {
constructor () {
this.dic = {}
}
count (number) {
if (number in this.dic) {
this.dic[number]++
} else {
this.dic[number] = 1
}
}
getCounts () {
// instead of doing the trailing comma stuff
// make an array of string and join the array
const out = Object.entries(this.dic).map(entry => entry.join(':')).join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(1)
counter.count(1)
counter.count(2)
counter.getCounts()
You may prefer using a Map since semantically you just want to map a number to a count.
class Counter {
constructor () {
this.dic = new Map()
}
count (number) {
const count = this.dic.get(number)
this.dic.set(number, count ? count + 1 : 1)
}
getCounts () {
const out = [...this.dic.entries()].map(entry => entry.join(':')).join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(1)
counter.count(1)
counter.count(2)
counter.getCounts()
Finally, if you want the number printed not in the order they were inserted but with the "int" order, you have to sort the entries:
class Counter {
constructor () {
this.dic = new Map()
}
count (number) {
const count = this.dic.get(number)
this.dic.set(number, count ? count + 1: 1)
}
getCounts () {
const out = [...this.dic.entries()]
.sort((a, b) => a[0] - b[0]) // sort by first key (which is the number)
.map(entry => entry.join(':'))
.join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(2) // insert 2 before
counter.count(1)
counter.count(1)
counter.getCounts()

Related

How can I extract all contained characters in a String? [duplicate]

I have a string with repeated letters. I want letters that are repeated more than once to show only once.
Example input: aaabbbccc
Expected output: abc
I've tried to create the code myself, but so far my function has the following problems:
if the letter doesn't repeat, it's not shown (it should be)
if it's repeated once, it's show only once (i.e. aa shows a - correct)
if it's repeated twice, shows all (i.e. aaa shows aaa - should be a)
if it's repeated 3 times, it shows 6 (if aaaa it shows aaaaaa - should be a)
function unique_char(string) {
var unique = '';
var count = 0;
for (var i = 0; i < string.length; i++) {
for (var j = i+1; j < string.length; j++) {
if (string[i] == string[j]) {
count++;
unique += string[i];
}
}
}
return unique;
}
document.write(unique_char('aaabbbccc'));
The function must be with loop inside a loop; that's why the second for is inside the first.
Fill a Set with the characters and concatenate its unique entries:
function unique(str) {
return String.prototype.concat.call(...new Set(str));
}
console.log(unique('abc')); // "abc"
console.log(unique('abcabc')); // "abc"
Convert it to an array first, then use Josh Mc’s answer at How to get unique values in an array, and rejoin, like so:
var nonUnique = "ababdefegg";
var unique = Array.from(nonUnique).filter(function(item, i, ar){ return ar.indexOf(item) === i; }).join('');
All in one line. :-)
Too late may be but still my version of answer to this post:
function extractUniqCharacters(str){
var temp = {};
for(var oindex=0;oindex<str.length;oindex++){
temp[str.charAt(oindex)] = 0; //Assign any value
}
return Object.keys(temp).join("");
}
You can use a regular expression with a custom replacement function:
function unique_char(string) {
return string.replace(/(.)\1*/g, function(sequence, char) {
if (sequence.length == 1) // if the letter doesn't repeat
return ""; // its not shown
if (sequence.length == 2) // if its repeated once
return char; // its show only once (if aa shows a)
if (sequence.length == 3) // if its repeated twice
return sequence; // shows all(if aaa shows aaa)
if (sequence.length == 4) // if its repeated 3 times
return Array(7).join(char); // it shows 6( if aaaa shows aaaaaa)
// else ???
return sequence;
});
}
Using lodash:
_.uniq('aaabbbccc').join(''); // gives 'abc'
Per the actual question: "if the letter doesn't repeat its not shown"
function unique_char(str)
{
var obj = new Object();
for (var i = 0; i < str.length; i++)
{
var chr = str[i];
if (chr in obj)
{
obj[chr] += 1;
}
else
{
obj[chr] = 1;
}
}
var multiples = [];
for (key in obj)
{
// Remove this test if you just want unique chars
// But still keep the multiples.push(key)
if (obj[key] > 1)
{
multiples.push(key);
}
}
return multiples.join("");
}
var str = "aaabbbccc";
document.write(unique_char(str));
Your problem is that you are adding to unique every time you find the character in string. Really you should probably do something like this (since you specified the answer must be a nested for loop):
function unique_char(string){
var str_length=string.length;
var unique='';
for(var i=0; i<str_length; i++){
var foundIt = false;
for(var j=0; j<unique.length; j++){
if(string[i]==unique[j]){
foundIt = true;
break;
}
}
if(!foundIt){
unique+=string[i];
}
}
return unique;
}
document.write( unique_char('aaabbbccc'))
In this we only add the character found in string to unique if it isn't already there. This is really not an efficient way to do this at all ... but based on your requirements it should work.
I can't run this since I don't have anything handy to run JavaScript in ... but the theory in this method should work.
Try this if duplicate characters have to be displayed once, i.e.,
for i/p: aaabbbccc o/p: abc
var str="aaabbbccc";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 ){
return obj;
}
}
).join("");
//output: "abc"
And try this if only unique characters(String Bombarding Algo) have to be displayed, add another "and" condition to remove the characters which came more than once and display only unique characters, i.e.,
for i/p: aabbbkaha o/p: kh
var str="aabbbkaha";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 && str.lastIndexOf(obj,i-1)==-1){ // another and condition
return obj;
}
}
).join("");
//output: "kh"
<script>
uniqueString = "";
alert("Displays the number of a specific character in user entered string and then finds the number of unique characters:");
function countChar(testString, lookFor) {
var charCounter = 0;
document.write("Looking at this string:<br>");
for (pos = 0; pos < testString.length; pos++) {
if (testString.charAt(pos) == lookFor) {
charCounter += 1;
document.write("<B>" + lookFor + "</B>");
} else
document.write(testString.charAt(pos));
}
document.write("<br><br>");
return charCounter;
}
function findNumberOfUniqueChar(testString) {
var numChar = 0,
uniqueChar = 0;
for (pos = 0; pos < testString.length; pos++) {
var newLookFor = "";
for (pos2 = 0; pos2 <= pos; pos2++) {
if (testString.charAt(pos) == testString.charAt(pos2)) {
numChar += 1;
}
}
if (numChar == 1) {
uniqueChar += 1;
uniqueString = uniqueString + " " + testString.charAt(pos)
}
numChar = 0;
}
return uniqueChar;
}
var testString = prompt("Give me a string of characters to check", "");
var lookFor = "startvalue";
while (lookFor.length > 1) {
if (lookFor != "startvalue")
alert("Please select only one character");
lookFor = prompt(testString + "\n\nWhat should character should I look for?", "");
}
document.write("I found " + countChar(testString, lookFor) + " of the<b> " + lookFor + "</B> character");
document.write("<br><br>I counted the following " + findNumberOfUniqueChar(testString) + " unique character(s):");
document.write("<br>" + uniqueString)
</script>
Here is the simplest function to do that
function remove(text)
{
var unique= "";
for(var i = 0; i < text.length; i++)
{
if(unique.indexOf(text.charAt(i)) < 0)
{
unique += text.charAt(i);
}
}
return unique;
}
The one line solution will be to use Set. const chars = [...new Set(s.split(''))];
If you want to return values in an array, you can use this function below.
const getUniqueChar = (str) => Array.from(str)
.filter((item, index, arr) => arr.slice(index + 1).indexOf(item) === -1);
console.log(getUniqueChar("aaabbbccc"));
Alternatively, you can use the Set constructor.
const getUniqueChar = (str) => new Set(str);
console.log(getUniqueChar("aaabbbccc"));
Here is the simplest function to do that pt. 2
const showUniqChars = (text) => {
let uniqChars = "";
for (const char of text) {
if (!uniqChars.includes(char))
uniqChars += char;
}
return uniqChars;
};
const countUnique = (s1, s2) => new Set(s1 + s2).size
a shorter way based on #le_m answer
let unique=myArray.filter((item,index,array)=>array.indexOf(item)===index)

Why does my outer variable not change when using a while loop?

function ranNum(value) {
return Math.ceil(Math.random() * value)
}
function createRanId(value) {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('')
const numbers = '0123456789'.split('')
const idLength = value || 6
let id = ''
for(let i = 0; i < idLength; i++) {
const numOrAlpha = ranNum(2)
numOrAlpha === 1 ? id += alphabet[ranNum(alphabet.length - 1)] : id += numbers[ranNum(numbers.length - 1)]
}
return id
}
function isAllNumbers(arr) {
return arr.every(value => Number.isInteger(value))
}
function allNumberId() {
let count = 0
let ranNum = createRanId(2).split("");
while(!isAllNumbers(ranNum)) {
ranNum = createRanId(2).split("")
count++
}
return [count, ranNum]
}
console.log(allNumberId())
So what im doing is generating a random string that consists of numbers and letters (for example: 3e3jjf). What i'm trying to achieve is to find a generated combination that only consists of numbers (for example: 235033). However, my code doesnt seem to work and ends up in an infinite loop. I'm making a thinking error somewhere in the function allNumberId
edit: this is obviously not production code or anything. I'm just practicing some javascript. It bugs me that I cant find what I do wrong here.
In your code you are checking for a number
Number.isInteger("6")
When it is a string it is false. So you need to alter your code to try to make it into a number or other option is isNaN()
function ranNum(value) {
return Math.ceil(Math.random() * value)
}
function createRanId(value) {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('')
const numbers = '0123456789'.split('')
const idLength = value || 6
let id = ''
for(let i = 0; i < idLength; i++) {
const numOrAlpha = ranNum(2)
numOrAlpha === 1 ? id += alphabet[ranNum(alphabet.length - 1)] : id += numbers[ranNum(numbers.length - 1)]
}
return id
}
function isAllNumbers(arr) {
return arr.every(value => Number.isInteger(+value))
}
function allNumberId() {
let count = 0
let ranNum = createRanId(2).split("");
while(!isAllNumbers(ranNum)) {
ranNum = createRanId(2).split("")
count++
}
return [count, ranNum]
}
console.log(allNumberId())
Your check could also be done as
const isInvalid = yourString.split("").map(Number).some(isNaN)
The problem is the usage of Number.isInteger. You're actually passing strings there (single-character strings consisting of a digit or a alphabet char), which is never a number (integer or not) so Number.isInteger always returns false and your isAllNumbers function doesn't recognice what it should.

print like this * "a" -> "a1" * "aabbbaa" -> "a2b3a2"

I am new to js.
can you tell me how to print like this * "a" -> "a1" * "aabbbaa" -> "a2b3a2"
i tried with hash map but test cases failing.
providing my code below.
i am not good in hash map.
can you tell me how to solve with hash map so that in future I can fix it my self.
not sure what data structure to use for this one.
providing my code below.
const _ = require("underscore");
const rle = ( input ) => {
console.log("input--->" + input);
//var someString ="aaa";
var someString = input;
var arr = someString.split("");
var numberCount = {};
for(var i=0; i< arr.length; i++) {
var alphabet = arr[i];
if(numberCount[alphabet]){
numberCount[alphabet] = numberCount[alphabet] + 1;
}
else{
numberCount[alphabet] = 1;
}
}
console.log("a:" + numberCount['a'], "b:" + numberCount['b']);
}
/**
* boolean doTestsPass()
* Returns true if all the tests pass. Otherwise returns false.
*/
/**
* Returns true if all tests pass; otherwise, returns false.
*/
const doTestsPass = () => {
const VALID_COMBOS = {"aaa": "a3", "aaabbc":"a3b2c1"};
let testPassed = true;
_.forEach(VALID_COMBOS, function(value, key) {
console.log(key, rle(key));
if (value !== rle(key)) {
testPassed = false;
}
});
return testPassed;
}
/**
* Main execution entry.
*/
if(doTestsPass())
{
console.log("All tests pass!");
}
else
{
console.log("There are test failures.");
}
You could
match groups of characters,
get the character and the count and
join it to a string.
function runLengthEncoding(string) {
return string
.match(/(.)\1*/g) // keep same characters in a single string
.map(s => s[0] + s.length) // take first character of string and length
.join(''); // create string of array
}
console.log(['a', 'aaa', 'aaabbc'].map(runLengthEncoding));
This is a bit more understandable version which iterates the given string and count the characters. If a different character is found, the last character and count is added to the result string.
At the end, a check is made, to prevent counting of empty strings and the last character cound is added to the result.
function runLengthEncoding(string) {
var result = '',
i,
count = 0,
character = string[0];
for (i = 0; i < string.length; i++) {
if (character === string[i]) {
count++;
continue;
}
result += character + count;
character = string[i];
count = 1;
}
if (count) {
result += character + count;
}
return result;
}
console.log(['', 'a', 'aaa', 'aaabbc'].map(runLengthEncoding));
You can reduce the array into a multidimensional array. map and join the array to convert to string.
const rle = (input) => {
return input.split("").reduce((c, v) => {
if (c[c.length - 1] && c[c.length - 1][0] === v) c[c.length - 1][1]++;
else c.push([v, 1]);
return c;
}, []).map(o => o.join('')).join('');
}
console.log(rle("a"));
console.log(rle("aabbbaa"));
console.log(rle("aaaaaa"));
Your function rle doesn't return a result.
Also note, this implementation may pass the test cases you wrote, but not the examples you mentioned in your question: for the string "aabbaa" this will produce "a4b2", not " a2b2a2" .
A simpler solution:
function runLengthEncoding(str) {
let out = "";
for (let i = 0; i < str.length; ++i) {
let temp = str[i];
let count = 1;
while (i < str.length && str[i+1] == temp) {
++count;
++i;
}
out += temp + count;
} // end-for
return out;
}
console.log(runLengthEncoding("a"));
console.log(runLengthEncoding("aabbbaa"));
console.log(runLengthEncoding("aaaaaa"));

How to compare every number in an array against each other? (javascript)

I have a set of numbers which are displayed like followed;
var data = "615:415,600:400,600:400,300:300"
Each number represents an x/y coordinate, and I would like to add a value next to each one which is calculated based on the frequency of the number within a range.
So, I would like to be able to compare each value against all others in this string, and from this perform the following functions;
Remove the number from the string if it is a duplicate, and add :1
If the x/y numbers are both within a range of 15 against any other number, add:1
If there are no matches, add :0
Turn into array
So using the data string, it would be transformed to;
var data = "615:415:1, 600:400:2, 300:300:0"
I have been trying to do this using a reducer function, but I'm struggling with mainly step 2. I'm hoping someone can help out?
Thanks - Code + Plunk below!
http://plnkr.co/edit/zPW1844cLnUFAlEI77jq?p=preview
var result = [];
var data = "615:415,600:400,600:400,300:300"
var count = 0;
var reducer = function(p, c, i, a) {
if (p && p !== c) {
var _t = p.split(":");
result.push({
x: _t[0],
y: _t[1],
value: count
});
count = 0;
if (i === a.length - 1) {
_t = c.split(":");
result.push({
x: _t[0],
y: _t[1],
value: count
});
}
}
else {
count++;
}
return c
}
data.split(',').sort().reduce(reducer);
console.log(result)
You could use a step-by-step approach and split the string first in coordinates, generate a hash table for the coordinates with count and filter only unique coordinates.
Then compare each unique coordinates with each other and count if inside of a given range.
Later map the coordinates with the count and join to string.
var data = "615:415,600:400,600:400,300:300",
result = function (array) {
var i, j,
hash = Object.create(null),
unique = array.split(',').filter(function (a) {
var parts = a.split(':');
if (!hash[a]) {
hash[a] = [parts[0], parts[1], 0]; // [x, y, count]
return true;
}
hash[a][2]++;
});
for (i = 0; i < unique.length - 1; i++) {
for (j = i + 1; j < unique.length; j++) {
if (
Math.abs(hash[unique[i]][0] - hash[unique[j]][0]) <= 15 &&
Math.abs(hash[unique[i]][1] - hash[unique[j]][1]) <= 15
) {
hash[unique[i]][2]++;
hash[unique[j]][2]++;
}
}
}
return unique.map(function (a) {
return hash[a].join(':');
}).join(', ');
}(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an alternative:
var data = "615:415,600:400,600:400,300:300";
var result = (function (s) {
var result = {};
var values = [];
// Process each value
s.split(',').forEach(function (v) {
var b = v.split(':');
// If a match, increment count by 2 (once for match and again for within 15)
if (result[v]) {
result[v].count += 2;
// Otherwise, just check for within 15
} else {
result[v] = {x:b[0], y:b[1], count:0};
values.forEach(function(xy, i){
if (xy[0]>= (b[0]-15) && xy[0] <= (+b[0]+15) &&
xy[1]>= (b[1]-15) && xy[1] <= (+b[1]+15) ) {
++result[xy.join(':')].count; // Increment for nearby only
}
})
values.push([b[0],b[1]]);
}
})
// Create required string format
return Object.keys(result).reduce(function(arr, key){
arr.push(key + ':' + result[key].count);
return arr;
},[]).join(', ');
})(data)
console.log(result);
All answers so far are good. I just would like to introduce a little variety by inventing an Array.prototype.withEachOther() method. Which just takes a callback an invokes the callback with each other item of the array being it's arguments as you may suggest. It works in place.
Array.prototype.withEachOther = function(cb){
this.map(function(e,i,a){
var t = a.slice();
t.splice(0,i+1);
t.map(function(f){
a[i] = cb(e,f);
});
});
return this;
};
var data = "615:415,600:400,600:400,300:300, 550 : 550".split(/\s*,\s*/)
.map(s => s.split(/\s*:\s*/).concat(0)),
cb = (f,s) => (Math.abs(f[0]-s[0]) <= 15 && Math.abs(f[1]-s[1]) <= 15 && (f[2]++, s[2]++),f);
result = data.reduceRight(function(p,c,i,a){
var fi = a.slice(0,i-a.length)
.findIndex(f => f[0] === c[0] && f[1] === c[1]);
fi !== -1 ? (a[fi][2] += ++c[2], a.splice(i,1))
: p.push(c);
return p;
},[])
.withEachOther(cb)
.reduce((p,c) => p += c[0]+":"+c[1]+":"+c[2]+", ","");
console.log(result);

ARRAY javascript indexOf

I have a multi-dimensional array. I need some sort of loop to randomly pick a string in the array and check to see if that string contains a character. If it doesn't, then repeat until it finds one.
I wrote a for loop that checks the array for the string, BUT it starts from 0 and works its way through the array until it finds it. I need it randomly pick a string in the array...any help?
jsFiddle --> http://jsfiddle.net/hz2MZ/1/
jQuery
$(document).ready(function() {
var myarr = [{"Name":"Bob", "Char":"134"},
{"Name":"Phil", "Char":"134"},
{"Name":"Jane", "Char":"1"},
{"Name":"Don", "Char":"4"},
{"Name":"Dan", "Char":"2"},
{"Name":"Jan", "Char":"12"},
{"Name":"Bill", "Char":"24"},
{"Name":"Sam", "Char":"14"},
{"Name":"Jake", "Char":"23"},
{"Name":"Ben", "Char":"3"}];
$('button').click(function() {
for(var i = 0; i < myarr.length; i++) {
if(myarr[i].Char.indexOf('2') !== -1) {
alert("Name: " + myarr[i].Name + "\nChar: " + myarr[i].Char);
return;
} else {}
}
});
});
My solution
$('button').click(function () {
var found = false;
while (!found) {
var randomIndex = Math.floor(Math.random() * myarr.length);
if (myarr[randomIndex].Char.indexOf('2') !== -1) {
alert("Name: " + myarr[randomIndex].Name + "\nChar: " + myarr[randomIndex].Char);
found = true;
} else {}
}
});
edit
Updated endless loop problem
$('button').click(function () {
var found = false;
var maxRandom = 20;
var currentRandom = 0;
while (!found && currentRandom < maxRandom) {
var randomIndex = Math.floor((Math.random() * myarr.length));
currentRandom++;
if (myarr[randomIndex].Char.indexOf('2') !== -1) {
alert("Name: " + myarr[randomIndex].Name + "\nChar: " + myarr[randomIndex].Char);
found = true;
} else {}
}
}
One approach is to copy the array, then randomly splice one member from the copy and check for the required value:
function randomValueByChar(arr, c) {
var a = arr.slice && arr.slice();
var i = a.length || 0;
var t;
while (i--) {
t = a.splice(Math.random() * i | 0, 1);
if (t[0].Char.indexOf(c) != -1) return t;
}
}
Another approach is to create an array of the indexes (e.g. [0,1,2,3…]), then randomly splice one value at a time and use it as an index to retrieve a value from the array. Both approaches will not have an infinite loop and will visit each member a maximum of once.

Categories

Resources