how to pick from an array depending on a certain number [closed] - javascript

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Lets say you use indexOf on an array to find all cases that say apple. If it comes back with three cases at positons 1, 2 and 3.
Now lets say there is an array = [10, 20, 30 ,40 ,50 ,60 ,70 ,80 ,90 ,100]
is there a way to use them cases 1,2 and 3 which come to 6. So is there a a way to go to position 6 "70" and produce that number. Depending on what the indexOf provides, is there a way to select that number from the array. Also if the indexOF produced 3.5 would there be a way to get 35 aka position 3.5.

If I understood correctly, you can use Array.reduce() to get the sum of the indexes where a match to some search term occurs. Then you can use the result from the reduce() to access your array of numbers:
let test = ["orange", "apple", "banana", "apple"];
let numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90];
const getSumOfIdxs = (arr, str) =>
{
return arr.reduce((acc, curr, idx) =>
{
if (curr === str)
{
if (acc < 0) acc += 1;
acc += idx;
}
return acc;
}, -1);
}
let res = getSumOfIdxs(test, "apple");
console.log("Sum of indexes: ", res);
console.log("Element from numbers: ", numbers[res]);
res = getSumOfIdxs(test, "hello");
console.log("Sum of indexes: ", res);
console.log("Element from numbers: ", numbers[res]);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Yes, you can - filter through the array and find all your apples, then map out the indexes from the original array, reduce to one index, and get the item at that index.
const arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
const appleNums = [20, 30, 40];
const resultingAppleNum = arr[arr.filter(e => appleNums.includes(e)).map(e => arr.indexOf(e)).reduce((acc, curr) => acc + curr)];
console.log(resultingAppleNum);

Related

How do I remove a random element from an array only ONCE? I keep getting more than 2 of each letter in my array [duplicate]

JavaScript
I've tried searching for something like this, but I am not able to find it.
It's a simple idea:
a. Take a random number between 0 to 10.
b. Let's say the random number rolled is a 3.
c. Then, save the number (the 3).
d. Now, take another random number again between 0 to 10, but it can't be the 3, because it has already appeared.
One solution is to generate an array (a "bucket") with all the values you want to pick, in this case all numbers from 0 to 10. Then you pick one randomly from the array and remove it from the bucket. Note that the example below doesn't check if the bucket is empty, so if you call the function below more than 10 times you will get an error.
var bucket = [];
for (var i=0;i<=10;i++) {
bucket.push(i);
}
function getRandomFromBucket() {
var randomIndex = Math.floor(Math.random()*bucket.length);
return bucket.splice(randomIndex, 1)[0];
}
// will pick a random number between 0 and 10, and can be called 10 times
console.log(getRandomFromBucket());
using d3:
var bucket = d3.shuffle(d3.range(11));
while(bucket.length) {
console.log(bucket.pop());
}
You can use something like this:
/**
* range Get an array of numbers within a range
* #param min {number} Lowest number in array
* #param max {number} Highest number in array
* #param rand {bool} Shuffle array
* #return {array}
*/
range: function( min, max, rand ) {
var arr = ( new Array( ++max - min ) )
.join('.').split('.')
.map(function( v,i ){ return min + i })
return rand
? arr.map(function( v ) { return [ Math.random(), v ] })
.sort().map(function( v ) { return v[ 1 ] })
: arr
}
And use it like so:
var arr = range( 1, 10, true )
Now you have an array with 10 numbers from 1 to 10 in random order and never repeated. So next you can do this:
arr.forEach(function( num, i ) {
// do something, it will loop 10 times
// and num will always be a different number
// from 1 to 10
});
Just for fun: derived from #Strilles answer a 'bucket constructor'
function RandomBucket(from,until){
min = (Number(from) || 0);
max = (Number(until) || 10)+1;
this.bucket = String(Array(max-min)).split(',').map(function(i){
return min++;
});
if (!RandomBucket.prototype.get){
RandomBucket.prototype.get = function(){
var randomValue =
this.bucket.length < 2
? this.bucket.shift()
: this.bucket.splice(Math.floor(Math.random()*this.bucket.length),1);
return randomValue || 'bucket empty';
};
}
}
See JsFiddle for usage example
Most of the time I'd stick with the method suggested by the other answers - i.e. create an array of possibilities, create a shuffled version of it, then take the first n values as your sample (all operations that are simple and general and can be implemented immutably).
However this isn't great if the range of possibilities is large compared to how much memory you want to use, or compared to how many random values you want to draw (although #Strilles solution uses the memory, but doesn't draw many random values, so is probably the best even for my usecase below).
A solution along the lines your question seems to suggest could look like this:
// select n integers from the range [from, to] (inclusive at both sides),
// don't use this approach for large values of n
// taking random values from the randomSource as needed
function randomNumbersWithoutReplacement(n, from, to, randomSource = Math.random) {
const result = [];
for (let i = 0; i < n; ++i) {
// i values have already been taken
// the +1 makes it inclusive
const rangeWidth = to - from - i + 1
let value = Math.floor(rangeWidth * randomSource()) + from
// correct the value compared to the already sampled integers
for (let j = 0; j < result.length; ++j) {
if (result[j] <= value) {
value++
}
}
result.push(value)
// sorting makes the correction loop simpler
// (and it's nice to report the result sorted too)
result.sort((a, b) => a - b)
}
return result
}
And why might you want this?
const quantumLottoNumbers = randomNumbersWithoutReplacement(6, 1, 59, quantumRandomSource)
Var rnd = getRnd();
While(rnd != lastRnd)
rnd = getRnd();
Where getRnd() is a function that generates your random number.
Actually, you would have to check if your current random number were in an array... And if your list of possible random numbers is small beware of an infinite loop.
Simply use the following function, which will draw a sample between 2 numbers based on sample size, and do so without replacement:
function random_sample_without_replacement(options) {
const arr = [];
while(arr.length < options.sample_size){
var r = Math.floor(Math.random() * options.population_size) + 1;
if(arr.indexOf(r) === -1) {
arr.push(r);
}
}
return(arr)
}
Usage:
random_sample = random_sample_without_replacement({
population_size : 1000,
sample_size : 100
})
[950, 725, 239, 273, 814, 325, 834, 702, 209, 740, 539, 281, 799, 459, 443, 758, 567, 124, 428, 462, 576, 234, 35, 344, 441, 580, 461, 371, 354, 616, 704, 233, 486, 296, 182, 63, 57, 357, 226, 969, 396, 879, 904, 718, 22, 121, 835, 52, 310, 359, 593, 793, 421, 870, 719, 959, 639, 755, 85, 10, 365, 189, 457, 895, 168, 574, 115, 176, 252, 284, 840, 721, 962, 780, 851, 71, 144, 827, 843, 643, 54, 246, 838, 100, 452, 303, 20, 572, 259, 102, 909, 471, 642, 8, 716, 388, 374, 338, 425, 880]
check to see if truly without replacement:
[...new Set(random_sample)].length
100
As a great person (Joma) once said, "Hashmap, I'll use a Hashmap!".
You can simply store the already taken values as object keys, and check every time you take a new one. If it's present you increase it in a loop until it becomes a not taken value. If it reaches the length, set it to zero.
function sample(options, count) {
if (options < count) {
throw new Error(
`Random sample error: can't sample ${count} items without repetition from ${options} options`
);
}
const result = [];
const exclude = {};
for (let i = 0; i < count; i++) {
let index = Math.floor(Math.random() * options);
while (exclude[index]) {
index += 1;
index %= options;
}
exclude[index] = true;
result.push(index);
}
return result;
}
sample(10, 10);
// [8, 4, 6, 5, 7, 9, 0, 1, 3, 2]
sample(10, 3);
// [1, 6, 7]
The computational cost of checking the next index isn't that big cause it uses an object instead of an array.
I don't know if you can determine the needed result size with antecedence, but if not, you can separate the inner for code and the exclude variable. Or even generate the entire sequence and just .pop() new values.
For a large space with picking just two numbers I think you can achieve this without a large array and still uniform probability (and fixed time - no while loop) by picking a number and an offset, something like:
const range = 1000000000;
const firstPick = Math.trunc(Math.random()*range);
// range -1 so the offset won't wrap
const offset= Math.trunc(Math.random()*(range-1));
const secondPick = (firstPick+offset)%range;
And for more than this I think you could accumulate the picks in sorted order and then adjust the subsequent picks by how many numbers were skipped past (if memory efficiency and runtime efficiency mattered) - though it would get more complex.

Is it possible to filter and modify the contents of a Javascript array in one go?

As the title states, I am wondering if it is possible to filter and array and then modify the values in one go? However, this is better explained with an example.
Imagine I have an array of numbers and I want to filter that array to include only positive numbers, then do some sort of calculation on the remaining numbers, say multiply by 10. Do I have to filter the array and then do an iteration to modify the values or can I do something like this?
const ages = [-243, -132, 112, 40, -96];
const filtered = ages.filter((number => (number > 0)) * 10);
You do this with single reduce function
const ages = [-243, -132, 112, 40, -96];
const result = ages.reduce((arr, num) => {
if (num > 0) arr.push(num * 10);
return arr;
}, []);
console.log(result);
or with single line function using flatMap
const ages = [-243, -132, 112, 40, -96];
const result = ages.flatMap((num) => (num > 0 ? [num * 10] : []));
console.log(result);
I don't think so.. I would do:
const ages = [-243, -132, 112, 40, -96];
const filtered = ages.filter(number => number > 0).map(x => x * 10);
console.log(filtered);
Short answer is Yes, you can.
You can filter and modify the array in only one go.
console.log(
[243 * 10, -132, 112, 40, -96]
.filter((age, index, arr) => {
arr[index+1] *= 10;
return age > 0;
}))
But I think we should avoid it as much as we can. Because it mutates the original array.

google script sheet, merging rows duplicate in array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
lets say i have a 3D array data with something like this (dynamically duplicates, sometimes 2-5 records) after processing from a sheet get range and wanted to ouput a certain value format to a new sheet
[abc, 123, 456]
[abc, 123, 567]
[abc, 123, 644]
[xyz, 345, 434]
[xyz, 334, 345]
[aaa, 124, 433]
[aaa, 124, 435]
my goal now is to merge the result into something like this before writing to the sheet
[abc, 123, 456:567:644]
[xyz, 345, 434]
[xyz, 334, 345]
[aaa, 124, 433:435]
that is the result i wanted to have and write it to 3 column sheet, delimiter for : is actually , but i put : cause don't want to make it confuse in array form. as long as the first and 2nd column values are the same, we should make it become one row but concatenating the 3rd column values of the duplicate records into one row instead.
is it better to make a duplicate array and compare them and push to a new array with concatenating the 3rd column values together?
I would do something like this.
let rows = [
['abc', 123, 456],
['abc', 123, 567],
['abc', 123, 644],
['xyz', 345, 434],
['xyz', 334, 345],
['aaa', 124, 433],
['aaa', 124, 435],
['abc', 123, 5672],
]
function mergeRow (rows) {
let newRows = []
let matched = []
for (var i = 0; i < rows.length; i++) {
if (!matched.includes(i)) {
let a = rows[i]
let nextIndex = i+1
let matches = []
for (var x = nextIndex; x < rows.length; x++) {
if (a[0] === rows[x][0] && a[1] === rows[x][1]) {
matches.push(x);
}
}
let newRow = a
let lastItem = a[2]
matches.forEach(index => {
lastItem += ':' + rows[index][2]
matched.push(index)
})
newRow[2] = lastItem
newRows.push(newRow)
}
}
console.log('newRows', newRows)
}
mergeRow(rows);

Array of Objects check for equality and place accordingly [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have a very interesting Problem and would like to hear some approaches that you would take.
Scenario:
A Tournament of 32 Players, each represented in an Array of Objects ex:
[
{ player: 'Badgy', points: 5, place: tba, reward: 0 },
{ player: 'Ceff', points: 5, place: tba, reward: 0},
{ player: 'Niclas', points: 10, place: tba, reward: 0}
]
Now there are prices defined for each of the top places like:
1 Place = 100 Coins
2 Place = 50 Coins
3 Place = 10 Coins
Now in this example 'Ceff' and 'Badgy' have the same point amount, which means they both have to be place 2 and get the reward of (place2 + place3) / 2, each of them would get 30 coins in this example.
Now I tried around but I have a hard time finding a good solution to this case, specially if a 3+ way tie happens.
You could take a sum and an index for the first found group of same points and take the average for the last same group.
var prices = [100, 50, 20, 15, 10, 5, 2, 1],
points = [ 20, 10, 10, 5, 5, 5, 4, 3],
sum = 0,
index,
profit = points.reduce((r, v, i, { [i - 1]: last }) => {
if (last !== v) {
sum = 0;
index = i;
}
sum += prices[i];
var avg = sum / (i - index + 1);
while (i >= index) r[i--] = avg;
return r;
}, []);
console.log(profit);

Find closest match JSON array [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 5 years ago.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Improve this question
I have a JSON dataset,
{
"a": ["73.0", "41.0", "98.0", "43.0"],
"s": ["74.0", "43.0", "112.0", "44.0"],
"f": ["75.0", "45.0", "116.0", "45.0"],
"l": ["76.0", "47.0", "120.0", "46.0"],
"x": ["77.0", "49.0", "128.0", "47.0"],
"q": ["78.0", "51.0", "134.0", "48.0"]
}
I want to closest match with a test data e.g. below, closest match is the array in the dataset which has lowest average difference related to the test array. In this example the closest match is "a": ["73.0", "41.0", "98.0", "43.0"],
{
"t": ["75.0", "42.0", "100.0", "44.0"]
}
Any library which can help with this? btw doing in JS.
This answer is based on the assumption that:
Your json is parsed and normalized so that all array values are numbers
You intend to measure difference in absolute numbers: diff(a, b) => Math.abs(a - b)
You want to find the key of the property pointing to the array that matches closest
// We're gonna get the closest match from here
const parsedJson = {
a: [73, 41, 98, 43],
s: [74, 43, 112, 44],
f: [75, 45, 116, 45],
l: [76, 47, 120, 46],
x: [77, 49, 128, 47],
q: [78, 51, 134, 48],
};
// We're gonna find the closest match to this
const comparator = [75, 42, 100, 44];
// Gets the average value of all given numbers
const avg = (...numbers) => numbers.reduce((acc, n) => acc + n, 0) / numbers.length;
// Returns an array with the absolute numeric difference
// between corresponding indices within 2 given arrays of
// numbers.
const difference = (arr1, arr2) => arr1.map((num, i) => Math.abs(num - arr2[i]));
// Returns the key of source that contains the array that
// matches closest to predicate.
const closestMatch = (comparator, source) => {
const [keyOfClosestMatch] = Object
.keys(source)
.map(key => [key, avg(...difference(comparator, source[key]))] )
.reduce((lowestSoFar, nextPredicate) => {
return lowestSoFar[1] < nextPredicate[1]
? lowestSoFar
: nextPredicate;
});
return keyOfClosestMatch;
}
let closestKey = closestMatch(comparator, parsedJson);
document.body.innerText = `Closest: "${closestKey}": [${parsedJson[closestKey]}]`;
You could iterate the keys and the items and check the absolute difference for taking the closest value for the result array.
var data = { a: ["73.0", "41.0", "98.0", "43.0"], s: ["74.0", "43.0", "112.0", "44.0"], f: ["75.0", "45.0", "116.0", "45.0"], l: ["76.0", "47.0", "120.0", "46.0"], x: ["77.0", "49.0", "128.0", "47.0"], q: ["78.0", "51.0", "134.0", "48.0"] },
test = ["75.0", "42.0", "100.0", "44.0"],
result = Object.keys(data).reduce(function (r, k, j) {
data[k].forEach(function (a, i) {
if (!j || Math.abs(a - test[i]) < Math.abs(r[i] - test[i])) {
r[i] = a;
}
});
return r;
}, []);
console.log(result);

Categories