Related
Sorry if this is a duplicate or a dumb question!
Basically, I need to get the count for duplicate values in an array until the next value changes. I can't use reduce() in my project, so any plain JS would be helpful.
let array = [a,a,a,b,b,b,b,b,c,c,c,a,d,d];
Results:
a:3,
b:5,
c:3,
a:1,
d:2
I would appreciate it very much.
You can use regex to get the desired result.
/([a-z])\1*/gi
let array = ["a", "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "a", "d", "d"];
const result = array
.join("")
.match(/([a-z])\1*/gi)
.map((s) => `${s[0]}${s.length}`);
console.log(result);
Simply loop over chars and check if char in dict then increment it else set it to 1;
let chars = ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'a', 'd', 'd'];
const dic={}
for(char of chars){
if(char in dic){
dic[char]++;
}else{
dic[char]=1;
}
}
console.log(dic);//{a: 4, b: 5, c: 3, d: 2}
Run iteration over array elements. Find the next non-equal current character's position. then difference the two indexes you will find the current character's continuous last position. Increase the iteration so you need not worry about multiple counts. If no non-equal character found, then the size of the sub-array is the rest of the main array size.
let ara = ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'a', 'd', 'd'];
let index = 0;
for (var i = 0; i < ara.length; i++) {
let char = ara[i];
let size = 0;
let nextIndex = ara.findIndex(
(a, index) => a !== char && index > i);
if (nextIndex < 0) {
size = ara.length - i;
i = ara.length - 1;
} else {
size = nextIndex - i;
i = nextIndex - 1;
}
console.log(char + ' ' + size);
Can anyone explain "slice" behavior?
var arr = ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arr.slice(0, 4)); // [ 'a', 'b', 'c', 'd' ]
console.log(arr.slice(4, 4)); // []
Why the second array is empty? Should not it be ['e','f'] ?
If omit "end" param, the result as expected.
arr.slice(4, 4) will return the items beginning at index = 4 to index = 4; 4-4=0 so your array has got a length of 0
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
var arr = ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arr.slice(0, 4)); // [ 'a', 'b', 'c', 'd' ]
console.log(arr.slice(4, 4)); // []
console.log(arr.slice(2, 4));
console.log(arr.slice(1, 2));
According to official docs it:
Returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.
Meaning that, you start at the index 4 and u end at that index, so literally you took no elements since the difference between start and end index is 0, so no elements are taken.
I'm coming from Python learning JavaScript. In Python, to find out if an item in one list is in another list, I can do something like:
haystack = ['a', 'b', 'c', 'd', 'e', 'f']
needle = ['a', 'b', 'e']
[i for i in haystack if i in needle]
In JavaScript, it seems the length of the list matters, as the code below returns ab rather than abe:
var needle = ["a", "b", "e"]
var haystack = ["a", "b", "c", "d", "e", "f"]
var found = "";
for (i=0; i < haystack.length; i++){
for (j=0; j < needle.length;j++){
if (needle[i] === haystack[j]) {
found+=needle[j]
}
}
}
console.log(found);
Python doesn't seem to care if the lists are of unequal lengths but JavaScript does. How can I do this in JavaScript?
You can do this
var needle = ["a", "b", "e"]
var haystack = ["a", "b", "c", "d", "e", "f"]
var list = needle.filter(word => haystack.indexOf(word) >= 0);
Here in this solution you don't have to iterate over length of the array explicitly. And it returns an array of filtered words.
Other choices
var list = needle.filter(word => haystack.includes(word));
The second one being not supported in IE.
You have done the indexing wrong
if (needle[i] === haystack[j])
will be
if (needle[j] === haystack[i])
And also instead of adding those strings into the array you have formed another string by concatenating them.
You've got the total right idea- the problem is that your if statement is backwards, so it's not iterating through each element in haystack.
if (haystack[i] === needle[j]) {
Length does not matter, you loop through both lists. You just made a little mistake on
if (needle[i] === haystack[j]) {
found+=needle[j]
}
Since you loop through haystack using i and needle using j. When you use haystack[j] instead of haystack[i] your if clause only goes up to "c" on haystack It should've been:
if (needle[j] === haystack[i]) {
found+=needle[j]
}
I would do this:
function inArray(v, a){
for(i=0,l=a.length; i<l; i++){
if(a[i] === v){
return true;
}
}
return false;
}
function needlesInHaystack(needlesArray, haystackArray){
for(var i=0,n,a=[],l=needlesArray.length; i<l; i++){
n = needlesArray[i];
if(inArray(n, haystackArray))a.push(n);
}
return a;
}
var needles = ['a', 'b', 'e', 'g'], haystack = ['a', 'b', 'c', 'd', 'e', 'f'];
console.log(needlesInHaystack(needles, haystack).join(''));
So let's say I have a set of items:
['a', 'b', 'c', 'd', 'e']
and I want to generate a random set (order does not matter) from those choices:
['a', 'e', 'd', 'c']
which is child's play, but instead of it being unlikely to generate a uniform result:
['c', 'c', 'c', 'c']
compared to something less uniform like:
['a', 'b', 'e', 'd']
I want to make it equally likely that a uniform set can be generated as it is likely that a non-uniform set can be generated.
Edit:
The result I'm trying to express is not just ['c', 'c', 'c', 'c', 'c', 'c'] or ['d', 'd', 'd', 'd', 'd', 'd'] but also the areas in between those uniformities like ['c', 'c', 'c', 'c', 'c', 'a'] or ['d', 'd', 'd', 'd', 'd', 'b'] or ['c', 'c', 'c', 'c', 'b', 'a']. Making all of those uniform sets and the areas in-between equally likely as non-uniform results is what I find challenging to create. I'm at a loss for where to even begin creating a set generator that does that.
Further clarification:
So if I generate a set of 1000 items, I want it to be equally likely that the set is 90% uniform or 100% uniform or 80% uniform or 20% uniform.
How can/should this be done?
From what you're saying, you want to ignore the order of the elements in your random set, so if your original set was ab then the possible outcomes (ignoring order) would be aa, ab, bb, and you'd like to see each of those appearing with equal probability (of 1/3), no?
A brute-force solution to this would be:
generate all outcomes (see Finding All Combinations of JavaScript array values),
sort each of the results so the elements appear alphabetically,
remove duplicates (see Remove duplicates from an array of objects in javascript)
select one of the remaining results at random
So for example, with abc:
all combinations = [`aaa`, `aab`, `aac`
`aba`, `abb`, `abc`
`aca`, `acb`, `acc`
`baa`, `bab`, `bac`
`bba`, `bbb`, `bbc`
`bca`, `bcb`, `bcc`
`caa`, `cab`, `cac`
`cba`, `cbb`, `cbc`
`cca`, `ccb`, `ccc`]
sorted combinations = [`aaa`, `aab`, `aac`
`aab`, `abb`, `abc`
`aac`, `abc`, `acc`
`aab`, `abb`, `abc`
`abb`, `bbb`, `bbc`
`abc`, `bbc`, `bcc`
`aac`, `abc`, `acc`
`abc`, `bbc`, `bcc`
`acc`, `bcc`, `ccc`]
remove duplicates = [`aaa`, `aab`, `aac`,
`abb`, `abc`, `acc`,
`bbb`, `bbc`, `bcc`,
`ccc`]
then choose from these with equal probability of 1/10
EDIT The final output above gives a clue to a non-brute-force solution: for each item the letters that follow each letter are of equal or 'higher' value (alphabetically speaking). So 'b' will never be followed by 'a', and 'c' will never be followed by 'a' or 'b' and so on.
So we can recursively generate all the combinations like this (sorry it's in python, you'll have to translate to javascript):
r=['a','b','c','d']
def get_combos(bound, n):
global r
if n == 1:
return r[bound:]
result=[]
for i in range(bound,len(r)):
for combo in get_combos(i, n-1):
result.append(r[i]+combo)
return result
x = get_combos(0,len(r))
print(x) # ['aaaa', 'aaab', 'aaac', 'aaad', 'aabb', 'aabc', 'aabd', 'aacc', 'aacd', 'aadd', 'abbb', 'abbc', 'abbd', 'abcc', 'abcd', 'abdd', 'accc', 'accd', 'acdd', 'addd', 'bbbb', 'bbbc', 'bbbd', 'bbcc', 'bbcd', 'bbdd', 'bccc', 'bccd', 'bcdd', 'bddd', 'cccc', 'cccd', 'ccdd', 'cddd', 'dddd']
print(len(x)) # 35
This can in fact be done. Just get a random number, and if it is over 0.5 then generate a random set, otherwise generate an extreme set from a random index. See the following code:
function generateRandomOrExtremeSet(a) {
var n = Math.random();
var set = [];
if (n > 0.5)
for (var i = 0; i < a.length; ++i)
set[i] = a[Math.round(Math.random()*(a.length-1))];
else {
var index = Math.round(Math.random() * (a.length-1));
for (var i = 0; i < a.length; ++i) {
if (Math.random() > 0.8) // change to adjust extremeness
set[i] = a[Math.round(Math.random()*(a.length-1))];
else
set[i] = a[index];
}
}
return set;
}
Here's a simple snippet that gets it done, using the strategy of first deciding whether to generate an extreme or non-extreme set.
const choices = ['a', 'b', 'c', 'd', 'e']
const repeatN = (times, x) => {
const ret = [];
while (times--) {
ret.push(x);
}
return ret;
}
const chooseN = (n, choices) => {
let list = choices.slice();
let ret = [];
while (n-- && list.length) {
let i = Math.floor(Math.random() * list.length);
ret.push(list[i]);
}
return ret;
};
const set = Math.random() > 0.5 ?
chooseN(5, choices) :
repeatN(5, chooseN(1, choices)[0]);
console.log(set);
Your original question seems to be stated incorrectly, which is why you are getting so many incorrect responses. In fact, the simple approach will give you the result that you want. You should do this by choosing a random value from your original set, like this:
function randomSet(set, size) {
let result = []
for (let i = 0; i < size; i++) {
// get a random index from the original set
let index = Math.floor(Math.random() * set.length)
result.push(set[index])
}
return result
}
console.log(randomSet(['a', 'b', 'c'], 3))
// These are all equally likely:
// ['a','a','a']
// ['a','b','b']
// ['a','b','c']
// ['c','b','a']
// ['a','b','a']
// ['a','a','b']
// ['b','a','a']
// ['b','a','b']
// ['b','b','b']
// etc.
Alternatively, it's possible that you are misunderstanding the definition of a set. Many of your examples like ['c', 'c', 'c', 'c', 'b', 'a'] are not sets, because they contain repeat characters. Proper sets cannot contain repeat characters and the order of their contents does not matter. If you want to generate a random set from your initial set (in other words, generate a subset), you can do that by picking a size less than or equal to your initial set size, and filling a new set of that size with random elements from the initial set:
function randomSet(set) {
let result = []
let size = Math.floor(Math.random() * set.length)
while(result.length !== size) {
// get a random index from the original set
let index = Math.floor(Math.random() * set.length)
// in this case, we construct the new set simply by removing items from the original set
result.splice(index, 1)
}
return result
}
console.log(randomSet(['a', 'b', 'c']))
// These are all equally likely:
// ['a','b','c']
// ['a','b']
// ['b','c']
// ['a','c']
// ['a']
// ['b']
// ['c']
// no other sets are possible
I have an array:
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
and I have an array of indices which I wish to remove:
var remove = [1, 3, 5]
so that the result is :
arr ==== ['A', 'C', 'E', 'G']
I can't do it with splice in a loop:
// WRONG
for (i = 0, l = remove.length; i < l; i++) {
arr.splice(remove[i]);
}
because after every iteration the index of each element has changed.
So how can I do this?
> arr.filter(function(x,i){return remove.indexOf(i)==-1})
["A", "C", "E", "G"]
To be more efficient, convert remove into an object/hashtable first, like so:
var removeTable = {}
remove.forEach(function(x){removeTable[x]=true})
> arr.filter(function(x,i){return removeTable[i]})
["A", "C", "E", "G"]
To not change your thinking too much- Start at the end.
A B C D E F..
When you remove element 5, it becomes..
A B C D E
Then you remove element 3, it becomes..
A B C E
Which is exactly what you want.
Count backwards:
// RIGHT
for (i = (remove.length-1); i >= 0; i--) {
arr.splice(remove[i]);
}
start the loop from last and remove the elements from highest index first.
As an alternative suggestion, you could use .push() to send the items you want to keep to a third array. See here for the basics. This would allow you to keep the original array intact, although it seems you don't want/need to do this.