Find most similar array of numbers - javascript

Let's say I have two arrays:
a=[168, 76, 62, 86]
b=[168, 80, 65, 90]
My input
[166.5, 75.5, 62, 86]
Now I want to get array "a" as my "result" because it is more similar to "a" than it is to "b".
How can I do that?

You could collect the absolute deltas and choose the one with the smaller error.
var array1 = [168, 76, 62, 86],
array2 = [168, 80, 65, 90],
input = [166.5, 75.5, 62, 86],
error = [array1, array2].map(function (a) {
return input.reduce(function (r, b, i) {
return r + Math.abs(a[i] -b);
}, 0);
});
console.log(error); // [2, 13] take the first one with smaller error.

Let's say that you've got two number arrays
x = [x1, x2, ..., xn]
y = [y1, y2, ..., yn]
The difference (i.e inverse similarity) between them is calculated by passing them through some error function E let's say
E = Σ (xi - yi)2
it's the same as calculating:
(x1 - y2)2 + ... + (xn - yn)2
= Σx2 + Σy2 - Σ(2xiyi)
= Σxi2 + Σyi2 - 2ΣxiΣyi
which means we can now use just the built-in Javascript methods .map() and .reduce().
const toSquare(ar) = ar.map( v => v*v )
const sum(ar) = ar.reduce( (acc, v) => acc + v )
function error(ar1, ar2) {
return sum(toSquare(ar1)) + sum(toSquare(ar2)) + 2*sum(ar1)*sum(ar2)
}
Much simpler:
you can also use underscore's or lodash's .zip function:
function error(ar1, ar2) {
return _.zip([ar1, ar2]).map( pair => Math.pow(p[0]-p[1], 2) ).reduce( (acc, v) => acc + v )
}

You will have to create a manual function to do that.
There is no built in way to do so.
function closest( inputArray, controlArrays) {
var margins;
for(var i=0, iMax=controlArrays.length; i < iMax; i++ ){
margins[i] = 0;
for(var j=0, jMax=inputArray.length; j < jMax; j++) {
//get the difference between the numbers and add it for the margins for that control array
margins[i] += Math.abs(inputArray[j]-controlArrays[j][i]);
}
}
//find smallest margin
var index = 0;
var value = margins[0];
for (var m = 1, mMax = temp.length; m < mMax; m++) {
if (temp[m] < value) {
value = temp[m];
index = m;
}
}
//return the smalles margin;
return controlArrays[index];
}

Related

Rearrange / transpose array items by keys diagonally in JavaScript

I have an array of strings, for example:
var arr=["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
To refer to any of these values, there are corresponding keys from 0 to 8, i.e. the arr[3] corresponds to "wolf". The amount of items of the actual array may vary and have more than 100 items in it. In this example there are 9 [0,1,2,3,4,5,6,7,8].
What I would like to accomplish is to rearrange the items by their keys diagonally, i.e. from:
[0,1,2,
3,4,5,
6,7,8]
into:
[0,2,5,
1,4,7,
3,6,8]
i.e. into [0,2,5,1,4,7,3,6,8], and thus also the sequence of the corresponding values from the original:
var arr=["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
resulting into the rearranged values:
var arr2=["dog", "bear", "hare", "cat", "lynx", "wolf", "owl", "sheep", "hen"];
The use of this solution would be implemented in more complex visualization of string items (strings each consisting of binary digits that correspond to UTF-8 encoded values of another data) in square shape, arranging them diagonally specifically from the left top corner. Thank you in advance!
It took some time for me to get the math right, but I was able to make a function which returns an Array of indexes in the correct order:
function getDiagonalArrayIndexes(length) {
const sqrt = Math.floor(Math.sqrt(length));
const formula = (x, y) => (y + x) * (y + x + 1) / 2 + x;
return Array.from({ length: sqrt*sqrt }, (_, i) => {
let x = i % sqrt, y = Math.floor(i / sqrt);
if (x + y < sqrt) {
return formula(x, y);
} else {
return length - 1 - formula(sqrt - 1 - x, sqrt - 1 - y);
}
})
// In case length's square root is not an integer
.concat(new Array(length - sqrt * sqrt).fill(null));
}
printSquare( getDiagonalArrayIndexes(9) );
printSquare( getDiagonalArrayIndexes(16) );
printSquare( getDiagonalArrayIndexes(25) ); /* Just for the demo */ function printSquare(n){const o=Math.sqrt(n.length),t=[];for(var e=0,a=0;e<n.length;e++)e>=o&&e%o==0&&a++,t[a]=t[a]||[],t[a].push(n[e]);console.log("[\n"+t.map(n=>n.map(n=>(" "+n).slice(-3)).join(",")).join(",\n")+"\n]")}document.body.innerHTML="<style>\n .as-console-wrapper { max-height: 100% !important; top: 0; }\n</style>";
You can then reuse it and map the indexes using your data:
function reorderDiagonally(arr) {
return getDiagonalArrayIndexes(arr.length)
.map(i => i !== null ? arr[i] : '');
}
var arr = ["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
console.log(JSON.stringify( reorderDiagonally(arr) )); /* Just for the demo */ function getDiagonalArrayIndexes(r){const t=Math.floor(Math.sqrt(r)),n=(r,t)=>(t+r)*(t+r+1)/2+r,l=Array.from({length:t*t},(l,a)=>{let e=a%t,o=Math.floor(a/t);return e+o<t?n(e,o):r-1-n(t-1-e,t-1-o)});return l.concat(new Array(r-l.length).fill(null))}
path method, gives valid diagonal path for given [row, col]
diagonals, aggregate paths for starting on first column and last row.
Simple map to shuffle based on the diagonal paths generated.
PS: Not tested the cases where array length is not perfect square.
const path = (row, col, len, res) => {
while (row > -1 && col < len) {
res.push([row, col]);
row--;
col++;
}
return res;
};
const diagonals = (len) => {
const res = [];
for (let i = 0; i < len; i++) {
path(i, 0, len, res);
}
for (let j = 1; j < len; j++) {
path(len - 1, j, len, res);
}
return res;
};
// const input = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const input = ["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"]
const len = Math.floor(Math.sqrt(input.length));
const output = [...input]
diagonals(len).map(([row, col], i) => output[row * len + col] = input[i]);
console.log(output.join(', '));

Javascript: key/value storage in object with multilevel nesting

I have been given two arrays:-
var keys = ['a.b', 'a.c.d', 'a.e', 'h[0]'];
var values = [10, 20, {}, 40];
The output that I want is:-
{
a: {
b: 10,
c: {
d: 20
},
e: {}
},
h: [40]
}
What I have tried so far is
let constructObject = (keys, values) => {
let output = {};
for(let i = 0; i<keys.length; i++) {
let props = keys[i].split('.');
for(let j=0; j<props.length;j++) {
if(props.length > (j+ 1)) {
if(output[props[j]] == undefined) {
output[props[j]] = {};
}
} else {
output[props[j]] = values[i];
}
}
}
return output;
}
The above code is not nesting deeper. I tried to store nested level key also using for loop but could not get any way to store keys, As javascript only gives way to store value not nested level keys.
You could separate the pathes into smaller parts, like properties and indices and build new object by respecting the next key and decide if an array or an object has to be taken.
function setValue(target, keys, value) {
const
isWrapped = s => s.startsWith('[') && s.endsWith(']'),
unwrap = s => isWrapped(s) ? s.slice(1, -1) : s,
path = keys.match(/\[[^\]+]\]|[^\.\[\]]+/g),
last = path.pop();
path
.reduce((o, k, i, { [i + 1]: next = last }) =>
o[unwrap(k)] = o[unwrap(k)] || (isWrapped(next) ? [] : {}), target)
[unwrap(last)] = value;
}
var keys = ['a.b', 'a.c.d', 'a.e', 'h[0]'],
values = [10, 20, {}, 40],
i,
l = keys.length,
result = {};
for (i = 0; i < l; i++) setValue(result, keys[i], values[i]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Generating m numbers that equal 100 but difference between two numbers have to be greater than 4

I'm trying to generate numbers that are equal 100(%) but all numbers have to be atleast 4 numbers from each other. So if I generate 4 numbers they have to be like this for example [22,28,15,35] and can't be like this [22,28,20,30] as difference between 28-30 and 22 - 20 is less than 4.
I was able to put together method for generating numbers that equal something which looks like this.
generate (max, many) {
this.numbers_array = []
this.normalized_numbers_array = []
this.sum = 0
for (let i = 0; i < many; i++) {
this.numbers_array.push(Math.random())
this.sum += this.numbers_array[i]
}
this.remaining = max
for (let i = 0; i < this.numbers_array.length; i++) {
this.outcome= (this.numbers_array[i] / this.sum) * max
this.normalized_numbers_array[i] = Math.floor(this.outcome)
this.remaining -= this.normalized_numbers_array[i]
if (i + 1 == this.numbers_array.length) {
while (this.remaining > 0) {
this.normalized_numbers_array[i]++
this.remaining--
}
}
}
}
And works fine. My next approach was trying to compare the normalized numbers with each other through two for loops and if statements. Then according to the differences between the numbers with each other I wanted add 2% to one and substruct 2% from other of the two numbers that are being compared. Then I put them in new array which I wanted to normalized again.
for (let i = 0; i < this.normalized_numbers_array.length; i++) {
for (let j = i + 1; j < this.normalized_numbers_array.length; j++) {
if (Math.abs(this.normalized_numbers_array[i] - this.normalized_numbers_array[j]) < 5) {
//do something
if (this.normalized_numbers_array[i] > this.normalized_numbers_array[j]) {
//do something
} else if (this.normalized_numbers_array[i] <= this.normalized_numbers_array[j]) {
//do something
}
}
}
}
This approach is flawed, though. As by adding or substructing I can make new differences that are less than 4. For example [35,18,26,21]->[35,16,26,23] Difference between 26 and 23 is 3 after the change.
My thought was to create another loop that would be going as long as there are differences. But I'm not sure that would work. So I was wondering whether there is better working solution for this problem- maybe being able to generate those numbers with differences in size from the start and not have to change them after.
Generate four uniform random numbers from the same range
Scale them so they add up to 76 (100 - separators, see below); adjust randomly as needed to account for the remainder
Insert separators: sort, then add 4 to the first, 8 to the second, 12 to the last one (and no adjustment to the zeroth).
Example
Generate [102, 387, 386, 284]
Scale: [102, 387, 386, 284] * 76 / (102 + 387 + 386 + 284) evaluates to [6, 25, 25, 18]
Adjust: That's only 74, so randomly add 1 to two elements: [6, 25, 26, 19]
Sort: [6, 19, 25, 26]
Insert separators: [6, 23, 33, 38]
Adds up to 100, guaranteed to be at least 4 apart, [EDIT] very few loops (to ensure no division by zero), and with as little arbitrary disturbance of the distribution. For the curious, here's what it looks like:
function f() {
let q, s;
while (!s) {
q = Array.from({length: 4}, () => Math.random());
s = q.reduce((a, e) => a + e);
}
q.forEach((e, i, q) => q[i] = (e * 76 / s)|0);
s = q.reduce((a, e) => a + e);
while (s < 76) {
q[(Math.random() * 4)|0]++; s++;
}
q.sort((a, b) => a - b);
q.forEach((e, i, q) => q[i] += i * 4);
return q;
}
const N = 100000;
function draw() {
const labels = ["#0", "#1", "#2", "#3", "Any"];
const colours = ["red", "orange", "green", "blue", "black"];
const data = Array.from({length:5}, (e, i) => ({
label: labels[i],
borderColor: colours[i],
backgroundColor: i == 4 ? "rgba(0, 0, 0, 0.1)" : "rgba(0, 0, 0, 0)",
pointRadius: 0,
data: Array.from({length:100}, (e, i) => ({ x: i, y: 0 }))
}));
for (let s = 0; s < N; s++) {
const q = f();
q.forEach((e, i) => {
data[i].data[e].y++;
data[4].data[e].y++;
});
}
const ctx = document.querySelector('canvas').getContext('2d');
const myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: data
},
options: {
maintainAspectRatio: false,
animation: false,
legend: {
position: 'right'
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}],
xAxes: [{
type: 'linear',
ticks: {
beginAtZero: true,
max: 100
}
}]
}
}
});
};
draw();
document.querySelector('canvas').addEventListener('dblclick', draw);
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0/dist/Chart.min.js"></script>
<canvas width="400" height="180"/>
<!DOCTYPE html>
<html>
<head>
</head>
<style>
.HideX{
display:none;
};
</style>
<body>
<input type="text" id="QQ">
<input type="text" id="QQ2">
<script>
function isEmpty(map) {
for(var key in map) {
if (map.hasOwnProperty(key)) {
return false;
}
}
return true;
}
function GetRandom(St,Range){
return Math.floor((Math.random() * Range) + St);
}
function Pick4(){
var BigPool={};
var ResultX=[];
//Generate [1,100]
for(var i=1;i<=100;i++){
BigPool[i.toString()]=i;
}
var Remain=100;
var Last=100;
for(var j=0;j<3;j++){
if(isEmpty(BigPool)){//Althoght impossible but add this exception
return Pick4();
}
var Pick=GetRandom(1,Remain);
if(BigPool.hasOwnProperty(Pick.toString())){
delete BigPool[Pick.toString()];//Remove Pick
ResultX.push(Pick);
Remain-=Pick;
for(var i=Remain+1;i<=Last;i++){
if(BigPool.hasOwnProperty(i.toString())){
delete BigPool[i.toString()];//Remove above remain
}
}
Last=Remain;
}else{
j--;
continue;
}
for(var i=-3;i<=3;i++){//Remove [Pick-3,Pick+3]
if(BigPool.hasOwnProperty((Pick+i).toString())){
delete BigPool[(Pick+i).toString()];//Remove [Pick-3,Pick+3]
}
}
}
if(BigPool.hasOwnProperty(Remain.toString())){
ResultX.push(Remain);
}else{
return Pick4();
}
return ResultX;
}
var G=Pick4();
document.getElementById("QQ").value = G;
document.getElementById("QQ2").value = G.reduce((a, b) => a + b, 0);
</script>
</body>
</html>
This is simple answer
A small brute force approach. After running, take a random result from the array.
function get4() {
function iter(temp, sum) {
var i, s, t;
for (i = 0; i <= max; i++) {
s = sum + i;
if (s > max) return;
t = temp.concat(i);
if (t.length === 4) {
if (s === max) result.push(t.map((v, i) => v + 4 * i));
continue;
}
iter(t, s);
}
}
var result = [],
max = 76;
iter([], 0);
return result;
}
var result = get4();
console.log(result.length);
console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is stats driven approach to the problem, might be useful to look at the problem from different perspective. We will use Multinomial distribution, which has natural property of sum being always equal to n (100 in your case). One could play with probabilities in such way, that difference between sampled numbers is very probable to be bigger than 4. Because mean values for multinomial are n*pi, we space probabilities far apart. After sampling array is sorted and checked for differences. If they are close, we reject the sample and call for another draw. I use https://github.com/jacobmenick/sampling code for multinomial sampling, there are other libraries as well.
Code, Node 12.1, Windows 10 x64
var multinom = SJS.Multinomial(100, [.02, .17, .33, .48]); // far away spacing of means n*p_i
q = multinom.draw().sort((a, b) => a - b); // sorted sample from multinomial
m = [] // array of pairwise differences, with first element removed
for (var k = 0; k < q.length - 1; ++k) {
var curr = q[k];
var next = q[k + 1];
m.push(next - curr);
}
reject = m.some(el => el < 4); // check for pairwise distance, if true reject sample and get another one
s = q.reduce((a, b) => a + b, 0); // check for sum, should be always 100
console.log(q);
console.log(m);
console.log(reject);
console.log(s);

Javascript - Generating all combinations of elements in a single array (in pairs)

I've seen several similar questions about how to generate all possible combinations of elements in an array. But I'm having a very hard time figuring out how to write an algorithm that will only output combination pairs. Any suggestions would be super appreciated!
Starting with the following array (with N elements):
var array = ["apple", "banana", "lemon", "mango"];
And getting the following result:
var result = [
"apple banana"
"apple lemon"
"apple mango"
"banana lemon"
"banana mango"
"lemon mango"
];
I was trying out the following approach but this results in all possible combinations, instead only combination pairs.
var letters = splSentences;
var combi = [];
var temp= "";
var letLen = Math.pow(2, letters.length);
for (var i = 0; i < letLen ; i++){
temp= "";
for (var j=0;j<letters.length;j++) {
if ((i & Math.pow(2,j))){
temp += letters[j]+ " "
}
}
if (temp !== "") {
combi.push(temp);
}
}
Here are some functional programming solutions:
Using EcmaScript2019's flatMap:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.flatMap(
(v, i) => array.slice(i+1).map( w => v + ' ' + w )
);
console.log(result);
Before the introduction of flatMap (my answer in 2017), you would go for reduce or [].concat(...) in order to flatten the array:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.reduce( (acc, v, i) =>
acc.concat(array.slice(i+1).map( w => v + ' ' + w )),
[]);
console.log(result);
Or:
var array = ["apple", "banana", "lemon", "mango"];
var result = [].concat(...array.map(
(v, i) => array.slice(i+1).map( w => v + ' ' + w ))
);
console.log(result);
A simple way would be to do a double for loop over the array where you skip the first i elements in the second loop.
let array = ["apple", "banana", "lemon", "mango"];
let results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (let i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (let j = i + 1; j < array.length; j++) {
results.push(`${array[i]} ${array[j]}`);
}
}
console.log(results);
Rewritten with ES5:
var array = ["apple", "banana", "lemon", "mango"];
var results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (var i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (var j = i + 1; j < array.length; j++) {
results.push(array[i] + ' ' + array[j]);
}
}
console.log(results);
In my case, I wanted to get the combinations as follows, based on the size range of the array:
function getCombinations(valuesArray: String[])
{
var combi = [];
var temp = [];
var slent = Math.pow(2, valuesArray.length);
for (var i = 0; i < slent; i++)
{
temp = [];
for (var j = 0; j < valuesArray.length; j++)
{
if ((i & Math.pow(2, j)))
{
temp.push(valuesArray[j]);
}
}
if (temp.length > 0)
{
combi.push(temp);
}
}
combi.sort((a, b) => a.length - b.length);
console.log(combi.join("\n"));
return combi;
}
Example:
// variable "results" stores an array with arrays string type
let results = getCombinations(['apple', 'banana', 'lemon', ',mango']);
Output in console:
The function is based on the logic of the following documentation, more information in the following reference:
https://www.w3resource.com/javascript-exercises/javascript-function-exercise-3.php
if ((i & Math.pow(2, j)))
Each bit of the first value is compared with the second, it is taken as valid if it matches, otherwise it returns zero and the condition is not met.
Although solutions have been found, I post here an algorithm for general case to find all combinations size n of m (m>n) elements. In your case, we have n=2 and m=4.
const result = [];
result.length = 2; //n=2
function combine(input, len, start) {
if(len === 0) {
console.log( result.join(" ") ); //process here the result
return;
}
for (let i = start; i <= input.length - len; i++) {
result[result.length - len] = input[i];
combine(input, len-1, i+1 );
}
}
const array = ["apple", "banana", "lemon", "mango"];
combine( array, result.length, 0);
I ended up writing a general solution to this problem, which is functionally equivalent to nhnghia's answer, but I'm sharing it here as I think it's easier to read/follow and is also full of comments describing the algorithm.
/**
* Generate all combinations of an array.
* #param {Array} sourceArray - Array of input elements.
* #param {number} comboLength - Desired length of combinations.
* #return {Array} Array of combination arrays.
*/
function generateCombinations(sourceArray, comboLength) {
const sourceLength = sourceArray.length;
if (comboLength > sourceLength) return [];
const combos = []; // Stores valid combinations as they are generated.
// Accepts a partial combination, an index into sourceArray,
// and the number of elements required to be added to create a full-length combination.
// Called recursively to build combinations, adding subsequent elements at each call depth.
const makeNextCombos = (workingCombo, currentIndex, remainingCount) => {
const oneAwayFromComboLength = remainingCount == 1;
// For each element that remaines to be added to the working combination.
for (let sourceIndex = currentIndex; sourceIndex < sourceLength; sourceIndex++) {
// Get next (possibly partial) combination.
const next = [ ...workingCombo, sourceArray[sourceIndex] ];
if (oneAwayFromComboLength) {
// Combo of right length found, save it.
combos.push(next);
}
else {
// Otherwise go deeper to add more elements to the current partial combination.
makeNextCombos(next, sourceIndex + 1, remainingCount - 1);
}
}
}
makeNextCombos([], 0, comboLength);
return combos;
}
The best solutions I have found - https://lowrey.me/es6-javascript-combination-generator/
Uses ES6 generator functions, I adapted to TS. Most often you don't need all of the combinations at the same time. And I was getting annoyed by writing loops like for (let i=0; ... for let (j=i+1; ... for (let k=j+1... just to get combos one by one to test if I need to terminate the loops..
export function* combinations<T>(array: T[], length: number): IterableIterator<T[]> {
for (let i = 0; i < array.length; i++) {
if (length === 1) {
yield [array[i]];
} else {
const remaining = combinations(array.slice(i + 1, array.length), length - 1);
for (let next of remaining) {
yield [array[i], ...next];
}
}
}
}
usage:
for (const combo of combinations([1,2,3], 2)) {
console.log(combo)
}
output:
> (2) [1, 2]
> (2) [1, 3]
> (2) [2, 3]
Just to give an option for next who'll search it
const arr = ['a', 'b', 'c']
const combinations = ([head, ...tail]) => tail.length > 0 ? [...tail.map(tailValue => [head, tailValue]), ...combinations(tail)] : []
console.log(combinations(arr)) //[ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ]
There are also this answer:
https://stackoverflow.com/a/64414875/19518308
The alghorithm is this answer generates all the possible sets of combination(or choose(n, k)) of n items within k spaces.
The algorhitm:
function choose(arr, k, prefix=[]) {
if (k == 0) return [prefix];
return arr.flatMap((v, i) =>
choose(arr.slice(i+1), k-1, [...prefix, v])
);
}
console.log(choose([0,1,2,3,4], 3));
I had a similar problem and this algorhitm is working very well for me.
Using map and flatMap the following can be done (flatMap is only supported on chrome and firefox)
var array = ["apple", "banana", "lemon", "mango"]
array.flatMap(x => array.map(y => x !== y ? x + ' ' + y : null)).filter(x => x)
I think it is an answer to all such questions.
/**
*
* Generates all combination of given Array or number
*
* #param {Array | number} item - Item accepts array or number. If it is array exports all combination of items. If it is a number export all combination of the number
* #param {number} n - pow of the item, if given value is `n` it will be export max `n` item combination
* #param {boolean} filter - if it is true it will just export items which have got n items length. Otherwise export all posible length.
* #return {Array} Array of combination arrays.
*
* Usage Example:
*
* console.log(combination(['A', 'B', 'C', 'D'], 2, true)); // [[ 'A','A' ], [ 'A', 'B' ]...] (16 items)
* console.log(combination(['A', 'B', 'C', 'D'])); // [['A', 'A', 'A', 'B' ],.....,['A'],] (340 items)
* console.log(comination(4, 2)); // all posible values [[ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 0, 0 ], [ 0, 1 ], [ 0, 2 ]...] (20 items)
*/
function combination(item, n) {
const filter = typeof n !=='undefined';
n = n ? n : item.length;
const result = [];
const isArray = item.constructor.name === 'Array';
const count = isArray ? item.length : item;
const pow = (x, n, m = []) => {
if (n > 0) {
for (var i = 0; i < count; i++) {
const value = pow(x, n - 1, [...m, isArray ? item[i] : i]);
result.push(value);
}
}
return m;
}
pow(isArray ? item.length : item, n);
return filter ? result.filter(item => item.length == n) : result;
}
console.log("#####first sample: ", combination(['A', 'B', 'C', 'D'], 2)); // with filter
console.log("#####second sample: ", combination(['A', 'B', 'C', 'D'])); // without filter
console.log("#####third sample: ", combination(4, 2)); // gives array with index number
Generating combinations of elements in an array is a lot like counting in a numeral system,
where the base is the number of elements in your array (if you account for the leading zeros that will be missing).
This gives you all the indices to your array (concatenated):
arr = ["apple", "banana", "lemon", "mango"]
base = arr.length
idx = [...Array(Math.pow(base, base)).keys()].map(x => x.toString(base))
You are only interested in pairs of two, so restrict the range accordingly:
range = (from, to) = [...Array(to).keys()].map(el => el + from)
indices = range => range.map(x => x.toString(base).padStart(2,"0"))
indices( range( 0, Math.pow(base, 2))) // range starts at 0, single digits are zero-padded.
Now what's left to do is map indices to values.
As you don't want elements paired with themselves and order doesn't matter,
those need to be removed, before mapping to the final result.
const range = (from, to) => [...Array(to).keys()].map(el => el + from)
const combinations = arr => {
const base = arr.length
return range(0, Math.pow(base, 2))
.map(x => x.toString(base).padStart(2, "0"))
.filter(i => !i.match(/(\d)\1/) && i === i.split('').sort().join(''))
.map(i => arr[i[0]] + " " + arr[i[1]])
}
console.log(combinations(["apple", "banana", "lemon", "mango"]))
With more than ten elements, toString() will return letters for indices; also, this will only work with up to 36 Elements.
Generating combinations is a classic problem. Here's my interpretation of that solution:
const combinations = (elements) => {
if (elements.length == 1) {
return [elements];
} else {
const tail = combinations(elements.slice(1));
return tail.reduce(
(combos, combo) => { combos.push([elements[0], ...combo]); return combos; },
[[elements[0]], ...tail]
);
}
};
const array = ["apple", "banana", "lemon", "mango"];
console.log(combinations(array));
Here is an non-mutating ES6 approach combining things (TS):
function combine (tail: any[], length: number, head: any[][] = [[]]): any[][] {
return tail.reduce((acc, tailElement) => {
const tailHeadVariants = head.reduce((acc, headElement: any[]) => {
const combination = [...headElement, tailElement]
return [...acc, combination]
}, [])
if (length === 1) return [...acc, tailHeadVariants]
const subCombinations = combine(tail.filter(t => t !== tailElement), length - 1, tailHeadVariants)
return [...acc, ...subCombinations]
}, [])
}
As this post is well indexed on Google under the keywords "generate all combinations", lots of people coming here simply need to generate all the unique combinations, regardless of the size of the output (not only pairs).
This post answers this need.
All unique combinations, without recursion:
const getCombos = async (a) => {
const separator = '';
const o = Object();
for (let i = 0; i < a.length; ++i) {
for (let j = i + 1; j <= a.length; ++j) {
const left = a.slice(i, j);
const right = a.slice(j, a.length);
o[left.join(separator)] = 1;
for (let k = 0; k < right.length; ++k) {
o[[...left, right[k]].join(separator)] = 1;
}
}
}
return Object.keys(o);
}
const a = ['a', 'b', 'c', 'd'];
const b = await getCombos(a);
console.log(b);
// (14) ['a', 'ab', 'ac', 'ad', 'abc', 'abd', 'abcd',
// 'b', 'bc', 'bd', 'bcd', 'c', 'cd', 'd']
This code splits the array into 2 sub arrays, left / right, then iterate over the right array to combine it with the left array. The left becomes bigger overtime, while the right becomes smaller. The result has only unique values.
Beating a dead horse a bit, but with smaller sets where recursion limit and performance is not a problem, the general combination generation can be done recursively with "recurse combinations containing the first element in given array" plus "recurse combinations not containing the first element". It gives quite compact implementation as a generator:
// Generator yielding k-item combinations of array a
function* choose(a, k) {
if(a.length == k) yield a;
else if(k == 0) yield [];
else {
for(let rest of choose(a.slice(1), k-1)) yield [a[0], ...rest];
for(let rest of choose(a.slice(1), k)) yield rest;
}
}
And even slightly shorter (and twice faster, 1 M calls of 7 choose 5 took 3.9 seconds with my MacBook) with function returning and array of combinations:
// Return an array of combinations
function comb(a, k) {
if(a.length === k) return [a];
else if(k === 0) return [[]];
else return [...comb(a.slice(1), k-1).map(c => [a[0], ...c]),
...comb(a.slice(1), k)];
}

How to find a nearest higher number from a specific set of numbers :javascript [duplicate]

This question already has an answer here:
How to find a same or nearest higher number from a specific set of numbers :javascript
(1 answer)
Closed 6 years ago.
I have a set of numbers & my requirements is to find same or nearest higher number to a specific variable
set/object of numbers
var person = {
A:107,
B:112,
C:117,
D:127,
E:132,
F:140,
G:117,
H:127,
I:132,
J:132,
K:140,
L:147,
M:117,
N:127,
O:132
};
I need to find a nearest higher number to vaiable x
eg1- if
x = 116;
then nearest higher number to x from number set is 117, which repeat at C, G, M
so I need to find out C, G, M programatically with javascript
eg2-
x= 127
then same number to x from number set repeat at D,H,N
so I need to find out D,H,N programatically with javascript
Thanks for Help
You can use reduce to find the lowest difference and collect the keys with that value. If a lower difference is found, the keys array is replaced with the new set of lower keys, e.g.
function getNextHighest(obj, value) {
var diff = Infinity;
return Object.keys(obj).reduce(function(acc, key) {
var d = obj[key] - value;
if (d > 0 && d < diff) {
diff = d;
acc = [key];
} else if (d == diff) {
acc.push(key)
}
return acc;
}, [])
}
var person = {A:107,B:112,C:117,D:127,E:132,F:140,G:117,
H:127,I:132,J:132,K:140,L:147,M:117,N:127,O:132
};
document.write(getNextHighest(person, 116));
document.write('<br>' + getNextHighest(person, 140));
I think this should work :
var resultObject = [];
function search(x, person){
for (var i=0; i < person.length; i++) {
if ((person[i].name === x) || (person[i].name === (x+i))) {
return person[i];
}
}
}
var result = search(x, person);
resultObject.push(result);
var x = resultObject.length;
while(x >0){
console.log(x+"\n");x--;
}
You can use a function like this:
function findNearestNumbers(x, person) {
var results = [];
var currentClosest;
// Difference function measures the difference
// between two numbers
var diff = function(a, b) {
return Math.abs(a - b);
}
// Loop through each number on the person
for(var i in person) {
// We don't even need to do the diff if x is greater
// than the current number
if(x > p[i]) {
continue;
}
// Calculate the differnce between the current
// Number and the supplied 'x' value
var d = diff(x, person[i]);
// We want the highest number, not a matching number.
if(diff === 0) {
continue;
}
if(!currentClosest || d < currentClosest) {
// If it's the closest number so far, create
// a new array to store the results, with this key as
// the first element
results = [i];
currentClosest = d;
}
else if(currentClosest === d) {
// If this is number is the same as the closest number
// then add this key to the results array
results.push(i);
}
}
return results;
}
Try the fiddle here https://jsfiddle.net/y4nu3t0d/4/
Try a series of sort and filter on the object:
var person = {
A: 107,
B: 112,
C: 117,
D: 127,
E: 132,
F: 140,
G: 117,
H: 127,
I: 132,
J: 132,
K: 140,
L: 147,
M: 117,
N: 127,
O: 132
};
var myNum = 117;
var nearest = Object.keys(person)
.sort(function(item1, item2) {
if (person[item1] > person[item2]) {return 1};
if (person[item1] < person[item2]) {return -1};
return 0;
}).filter(function(item) {
return person[item] >= myNum;
}).filter(function(item, index, list){
return person[list[0]] == person[item];
});
console.log(nearest) // will print ["C","M","G"]
Check this fiddle for a complete example.
Totally edited to satisfy my commenters =)
var x = 116;
var max = x;
for (var key in person) {
if(person[key]>max){
max = person[key];
}
}
function nextMax(ar, k, m) {
var dif = m; //Some high number
var rkey = null;
for (var key in ar) {
var check = ar[key]-k;
if(check<dif && check > 0){
dif = check;
rkey = key;
}
}
return(rkey);
}
var keys = [];
var values = [];
for(var ckey; ckey = nextMax(person, x, max); ){
if(ckey == null){
break;
}
keys.push(ckey);
values.push(person[ckey]);
x = person[ckey];
}
console.log(keys);
console.log(values);
check it working: https://jsfiddle.net/Aschab/q65wes0a/2/
Thi is a straight forward approach with an Array#forEach loop and a variable for keeping the delta and one for the keys.
function nearestHigher(object, v) {
var delta = -1,
keys = [];
Object.keys(object).forEach(function (k) {
var d = object[k] - v;
if (d > 0) {
if (!~delta || d < delta) {
delta = d;
keys = [k];
return;
}
d === delta && keys.push(k);
}
});
return keys;
}
var person = { A: 107, B: 112, C: 117, D: 127, E: 132, F: 140, G: 117, H: 127, I: 132, J: 132, K: 140, L: 147, M: 117, N: 127, O: 132 };
document.write('<pre>' + JSON.stringify(nearestHigher(person, 116), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(nearestHigher(person, 132), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(nearestHigher(person, 140), 0, 4) + '</pre>');
You can try something like this:
Note, if you just want keys, you can replace result[k] = obj[k]; to result.push(k) and make result an array.
var person = {A:107,B:112,C:117,D:127,E:132,F:140,G:117,H:127,I:132,J:132,K:140,L:147,M:117,N:127,O:132};
function searchNearestNum(obj, x){
var value = null;
var result = {};
Object.keys(obj).sort(function(a,b){
return obj[a]>obj[b]?1:(obj[a]<obj[b])?-1:0
}).forEach(function(k){
if(!value && obj[k]>x){
value = obj[k];
result[k] = obj[k];
}
else if(obj[k] === value){
result[k] = obj[k];
}
});
return result;
}
function getList(){
var searchValue = document.getElementById("txt").value;
if(searchValue && !isNaN(searchValue)){
searchValue = parseInt(searchValue);
print(searchNearestNum(person, searchValue));
}
}
function print(obj){
document.getElementById("result").innerHTML = "<pre>" + JSON.stringify(obj,0,4) + "</pre>";
}
<input id="txt"/>
<button onclick="getList()">get list</button>
<p id="result"></p>
Approach 1
Try this demo of a very simple algorithm
First step -> Push the values in an array as [key, value, difference between value and search]
var personArray = [];
for ( var key in person )
{
if ( ( person[key] - 116 ) > 0 )
{
personArray.push([key, person[key], person[key] - 116 ]);
}
}
Final Step -> Sort it by the difference value
personArray.sort( function(a,b){return a[2]-b[2]} );
console.log( personArray[0][1] );
Approach 2
To make it even more simpler, keep the handle of lowest difference
var personArray = [];
var lowestDifference = 0;
var nearestHigherValue = 0;
for ( var key in person )
{
var difference = person[key] - 116;
if ( difference > 0 && lowestDifference < difference )
{
lowestDifference = difference;
nearestHigherValue = person[key] ;
}
}
console.log("nearest higher value is " + nearestHigherValue );
Try utilizing for..in loop, Object.keys(), Array.prototype.map(), Array.prototype.reduce() to store values of person as property keys of new object. If property does not exist add property of person, else concatenate property of person at created object; filter properties by comparing as numbers; return concatenated string containing properties of original object where filtered number is greater than input parameter number
var person = {
A: 107, B: 112, C: 117, D: 127, E: 132,
F: 140, G: 117, H: 127, I: 132, J: 132,
K: 140, L: 147, M: 117, N: 127, O: 132
};
var res = function(n) {
var data = {};
for (var prop in person) data[person[prop]] = (data[person[prop]] || "") + prop;
return data[Object.keys(data).map(Number).reduce(function(a, b) {
return a > n ? a : b
})];
}
document.body.textContent = res(116) + "\n" + res(140)
Try this
function getNearest(x){
var keys=[];
for(var key in person){
if(person[key]==x+1)
keys.push(key)
}
return keys;
}
getNearest(116)

Categories