All variations of matrix - javascript

I am trying to write an algorithm for getting all possible variations of a matrix in JavaScript.
Here is what I want to achieve:
blue,red
male,female,other
small,medium,large
-----------------
blue,male,small
blue,male,medium
blue,male,large
blue,female,small,
blue,female,medium,
blue,female,large
blue,other,small,
blue,other,medium,
blue,other,large
red,male,small
red,male,medium
red,male,large
red,female,small,
red,female,medium,
red,female,large
red,other,small,
red,other,medium,
red,other,large
Any idea how this can be done?

Wht you want is called the Cartesian product of several lists. If you have a fixed set of lists, nested loops are an easy way to generate the Cartesian product.
You can generalize this for an arbitrary list by iterating through the lists in odometer style. (Each digit of the odometer may have a different range, though.)
Here's how:
function cartesian(m) {
const res = [];
const index = []; // current index
const max = []; // length of sublists
for (let i = 0; i < m.length; i++) {
index.push(0);
max.push(m[i].length);
}
for (;;) {
res.push(index.map((i, j) => m[j][i]));
let i = 0;
index[i]++;
while (index[i] == max[i]) {
index[i] = 0;
i++;
if (i == m.length) return res;
index[i]++;
}
}
}
Call it like this:
const m = [
["blue", "red"],
["male", "female", "other"],
["small", "medium", "large"],
];
const p = cartesian(m);
This creates one huge list of all possibilities, which probably isn't ideal. You can change the function by doing whatever you want to do with each possibilitiy where the code above pushes to current list to the result array. The code above changes the first item in each iteration, which is the opposite of what you show in your post.

You can iterate through arrays using reduce:
let data = ['blue','red']
let data2 =['male','female','other']
let data3 =['small','medium','large']
let result = data.reduce((acc,rec) => {
return acc.concat(data2.reduce((acc2, rec2) => {
return acc2.concat(data3.reduce((acc3,rec3) =>{
return acc3.concat([`${rec}, ${rec2}, ${rec3}`])
},[]))
},[]))
},[])
console.log(result)

Related

A function that returns an array which is the intersection of two other arrays

function arraysCommon(array1, array2) {
return array1.filter(x => array2.includes(x));
}
This function does not work the way I want it to.
For instance given array1 = [1,2,3,2,1] and array2 = [5,4,3,2,1]
it returns [1,2,3,2,1], since the elements 1,2,3 are seen in both arrays.
But I want it to return [1,2,3] in that order since 1,2,3 are seen only once in array2 and are treated as seperate entities.
So pretty much the functionality should be that
Each element in the first array can map to at most one element in the second array.
Duplicated elements in each array are treated as separate entities.
the first array determines the order
I have attempted to loop through the arrays and check and compare the number of duplicates in each array but I can't seem to get the logic working correctly. Is there a different way to approach this?
I've attached an image of two Venn diagrams that might clarify the difference
Unfortunately, it gets more complicated because you need to know what numbers you have already added. In this case you need a temporary array to hold the result. We also need to track if a number exists in the array two times.
Try this:
function arraysCommon(array1, array2) {
//Copy array2 by duplicating and spreading the elements into another array.
var copyArray2 = [...array2];
//Temperary Array
var temp = [];
for (let x of array1) {
//Check if the element is in the first array and not already added to the temp array
if (copyArray2.includes(x)) {
temp.push(x);
//Remove item from copy array2 so it cannot be used anymore
copyArray2.splice(copyArray2.indexOf(x), 1);
}
}
//Return the temp array
return temp;
}
console.log(arraysCommon([1,2,3,2,1], [5,4,3,2,1]))
console.log(arraysCommon([1,2,3,2,1], [2,2,3,3,4]))
With sorting and counting this should be possible. Since you are incrementing when you find similar characters, this should be okay:
const array1= [1,4,1,1,5,9,2,7];
const array2 = [1,8,2,5,1]
const array3 = [1,2,3,2,1];
const array4 = [5,4,3,2,1,2]
const array5 = [1,2,3,2,1];
const array6 = [2,2,3,3,4]
function arraysCommon(array1, array2) {
const ans = [];
array1.sort();
array2.sort();
let j = 0;
let i = 0;
while(i<array1.length && j<array2.length){
if(array1[i] === array2[j]){
ans.push(array1[i]);
i++;
j++;
}
else if(array2[i] > array1[j]){
i++;
}
else{
j++;
}
}
console.log(ans);
}
arraysCommon(array1,array2);
arraysCommon(array3,array4);
arraysCommon(array5,array6);
this should work as you wanted!
// test 1
const array1 = [1,4,1,1,5,9,2,7];
const array2 = [1,8,2,5,1];
// test 2
const array3 = [1,2,3,2,1];
const array4 = [5,4,3,2,1];
const mapper = (array1, array2) => {
var obj = {};
array1.forEach((x, indexX) => {
array2.forEach((y, indexY) => {
if (x == y) {
if (!Object.values(obj).includes(indexX) && !obj.hasOwnProperty(indexY)) {
obj[indexY] = indexX;
return;
}
}
})
})
return Object.values(obj).sort().map(values => array1[values]);
}
console.log(mapper(array1, array2));
console.log(mapper(array3, array4));
I hope this helps. Cheers.
You can instance a new Set, wich brings only unique values and than retorn a array from this set.
Something like this:
function arraysCommon(array1, array2) {
const filtered = array1.filter(x => array2.includes(x));
const uniqueValues = new Set(filtered)
return Array.from(uniqueValues)
}

Filter if array include an element of another array

I have two arrays like:
arr1 = ["orange","banana"]
arr2 = [{"fruit"= ["pineapple","banana"]},{"fruit"= ["grapes","apple"]},{"fruit"= ["apple","orange"]}]
And i need to filter if in second array exists an item of the first.
I am trying to do this:
let kFilter = arr1.map(itemY => { return itemY; });
let filteredK = arr2.filter(itemX => kFilter.includes(itemX.fruit));
But no success :-(
First to avoid too much iteration convert the simple array to an object something like following. Otherwise the complexity will be high
const fruitMap = {
"orange": 1,
"banana": 1
};
Then you have to iterate over each of the items in second array
arr2.forEach(fruitsObj => {
let arr = fruitsObj['fruit'];
for (let i = 0; i < arr.length(); i ++) {
if (fruitMap[arr[i]]) {
arr.splice(i, 1);
}
}
})
//declaration variables correctly
let arr1 = ["orange","banana"]
let arr2 = [{"fruit" : ["pineapple","banana"]},{"fruit" : ["grapes","apple"]},{"fruit": ["apple","orange"]}];
//filter using reduce
arr2.filter((e)=>arr1.reduce((a,i)=>a||e["fruit"].includes(i),false))
//filter using some
arr2.filter((e)=>arr1.some((i)=>e["fruit"].includes(i)))

Get all possible set of combinations of two arrays as an array of arrays with JavaScript

Please note: the linked question, "How can I create every combination possible for the contents of two arrays?" does not solve this particular question. The persons that labeled that did not fully understand this specific permutation and request.
If you have two arrays (arr1, arr2) with n elements in each array (i.e., each array will be the same length), then the question is: What's the best method to get/determine all the possible matches where elements do not match with other elements in the same array and where order does not matter?
For example, let's say I have:
arr1 = ["A","B","C"];
arr2 = ["Z","Y","X"];
I would like to get back an array of arrays where each element of one array is paired with an element of another array. So the result would be a unique set of arrays:
matches = [
[["A","Z"],["B","Y"],["C","X"]],
[["A","Z"],["B","X"],["C","Y"]],
[["A","Y"],["B","X"],["C","Z"]],
[["A","Y"],["B","Z"],["C","X"]],
[["A","X"],["B","Z"],["C","Y"]],
[["A","X"],["B","Y"],["C","Z"]],
]
Please note, these two arrays would be the same:
[["A","Z"],["B","Y"],["C","X"]]
[["B","Y"],["C","X"],["A","Z"]]
I am trying to do this with vanilla JavaScript but am completely open to using Lodash as well. For an added bonus, since this can get out of control, speed and performance are important. But right now, I am just trying to get something that would yield a proper result set. To limit this, this function would probably not be used with more than two arrays of 50 elements each.
Here is my latest attempt (using lodash):
function getMatches(arr1, arr2){
var matches = [];
for (var arr1i = 0, arr1l = arr1.length; arr1i < arr1l; arr1i++) {
for (var arr2i = 0, arr2l = arr2.length; arr2i < arr2l; arr2i++) {
matches.push(_(arr1).zip(arr2).value());
arr2.push(arr2.shift());
}
}
return matches;
}
[[A, 1], [B, 2]]
is the same as
[[B, 2], [A, 1]]
in your case, which means that the solution depends on what you pair to the first elements of your array. You can pair n different elements as second elements to the first one, then n - 1 different elements as second elements to the second one and so on, so you have n! possibilities, which is the number of possible permutations.
So, if you change the order of the array elements but they are the same pair, they are equivalent, so you could view the first elements as a fixed ordered set of items and the second elements as the items to permutate.
Having arr1 = [a1, ..., an] and arr2 = [b1, ..., bn] we can avoid changing the order of a1. So, you permutate the inner elements and treat the outer elements' order as invariant, like:
const permutations = function*(elements) {
if (elements.length === 1) {
yield elements;
} else {
let [first, ...rest] = elements;
for (let perm of permutations(rest)) {
for (let i = 0; i < elements.length; i++) {
let start = perm.slice(0, i);
let rest = perm.slice(i);
yield [...start, first, ...rest];
}
}
}
}
var other = ['A', 'B', 'C'];
var myPermutations = permutations(['X', 'Y', 'Z']);
var done = false;
while (!done) {
var next = myPermutations.next();
if (!(done = next.done)) {
var output = [];
for (var i = 0; i < next.value.length; i++) output.push([other[i], next.value[i]]);
console.log(output);
}
}
You're just looking for permutations. The first elements of your tuples are always the same, the second ones are permuted so that you get all distinct sets of combinations.
const arr1 = ["A","B","C"];
const arr2 = ["Z","Y","X"];
const result = permutate(arr2).map(permutation =>
permutation.map((el, i) => [arr1[i], el])
);
This implementation uses Typescript and Lodash.
const permutations = <T>(arr: T[]): T[][] => {
if (arr.length <= 2)
return arr.length === 2 ? [arr, [arr[1], arr[0]]] : [arr];
return reduce(
arr,
(acc, val, i) =>
concat(
acc,
map(
permutations([...slice(arr, 0, i), ...slice(arr, i + 1, arr.length)]),
vals => [val, ...vals]
)
),
[] as T[][]
);
};

Iterating over every pair of elements in a Map

I have a Map which holds Shape object values with their id as key.
I need to iterate over every pair of Shapes in this Map, but I want to iterate over each pair only once.
I know I can use forEach or for..of, but I can't find a way to prevent duplicate pairs. Also, this should be a as efficient as possible.
shapes.forEach((shape1, shapeId1) => {
shapes.forEach((shape2, shapeId2) => {
// iterating over each pair many times
});
});
I'd suggest first converting the Map to an array of its entries:
const entryArray = Array.from(shapes.entries());
Then you can choose to iterate pairs either via traditional for loop:
console.log("FOR LOOP");
for (let i = 0; i < entryArray.length; i++) {
const [shapeId1, shape1] = entryArray[i];
for (let j = i + 1; j < entryArray.length; j++) {
const [shapeId2, shape2] = entryArray[j];
console.log(shapeId1, shapeId2);
}
}
Or via the functional forEach array methods:
console.log("FOREACH");
entryArray.forEach(([shapeId1, shape1], i) =>
entryArray.slice(i + 1).forEach(([shapeId2, shape2]) => {
console.log(shapeId1, shapeId2);
})
);
In each case you are avoiding duplicates by the inner loop only iterating the elements after the outer loop index. I don't know what your Shape or id types look like, but given this:
interface Shape {
area: number;
}
const shapes: Map<string, Shape> = new Map([
["a", { area: 1 }],
["b", { area: 2 }],
["c", { area: 3 }]
]);
The above code outputs
FOR LOOP
a b
a c
b c
FOREACH
a b
a c
b c
So you can see that you get distinct pairs. Hope that helps; good luck!
Link to code
You can use two for iterations using an index and start the nested iteration from the root index + 1. This will ensure you that you will never process two pairs.
const arr = [1,2,3,4];
for (let i = 0; i<arr.length-1; i++) {
for (let j = i+1; j<arr.length; j++) {
console.log(`${arr[i]} - ${arr[j]}`)
}
}
Use a Set with the two indexes:
let indexes = new Set();
shapes.forEach((shape1, shapeId1) => {
shapes.forEach((shape2, shapeId2) => {
if (set.has(`${shapeId1}-${shapeId2}`) || set.has(`${shapeId2}-${shapeId1}`)) return;
set.add(`${shapeId1}-${shapeId2}`);
});
});

Algorithm to sort an array in JS

I have an array
var arr= [
["PROPRI","PORVEC"],
["AJATRN","PROPRI"],
["BASMON","CALVI"],
["GHICIA","FOLELI"],
["FOLELI","BASMON"],
["PORVEC","GHICIA"]
] ;
And I'm trying to sort the array by making the second element equal to the first element of the next, like below:
arr = [
["AJATRN","PROPRI"],
["PROPRI","PORVEC"],
["PORVEC","GHICIA"],
["GHICIA","FOLELI"],
["FOLELI","BASMON"],
["BASMON","CALVI"]
]
The context is : these are somes sites with coordinates, I want to identify the order passed,
For exemple, I have [A,B] [C,D] [B,C] then I know the path is A B C D
I finally have one solution
var rs =[];
rs[0]=arr[0];
var hasAdded=false;
for (var i = 1; i < arr.length; i++) {
hasAdded=false;
console.log("i",i);
for (var j = 0, len=rs.length; j < len; j++) {
console.log("j",j);
console.log("len",len);
if(arr[i][1]===rs[j][0]){
rs.splice(j,0,arr[i]);
hasAdded=true;
console.log("hasAdded",hasAdded);
}
if(arr[i][0]===rs[j][1]){
rs.splice(j+1,0,arr[i]);
hasAdded=true;
console.log("hasAdded",hasAdded);
}
}
if(hasAdded===false) {
arr.push(arr[i]);
console.log("ARR length",arr.length);
}
}
But it's not perfect, when it's a circle like [A,B] [B,C] [C,D] [D,A]
I can't get the except answer
So I really hope this is what you like to achieve so have a look at this simple js code:
var vector = [
["PROPRI,PORVEC"],
["AJATRN,PROPRI"],
["BASMON,CALVI"],
["GHICIA,FOLELI"],
["FOLELI,BASMON"],
["PORVEC,GHICIA"]
]
function sort(vector) {
var result = []
for (var i = 1; i < vector.length; i++) result.push(vector[i])
result.push(vector[0])
return (result)
}
var res = sort(vector)
console.log(res)
Note: Of course this result could be easily achieved using map but because of your question I'm quite sure this will just confuse you. So have a look at the code done with a for loop :)
You can create an object lookup based on the first value of your array. Using this lookup, you can get the first key and then start adding value to your result. Once you add a value in the array, remove the value corresponding to that key, if the key has no element in its array delete its key. Continue this process as long as you have keys in your object lookup.
var vector = [["PROPRI", "PORVEC"],["AJATRN", "PROPRI"],["BASMON", "CALVI"],["GHICIA", "FOLELI"],["FOLELI", "BASMON"],["PORVEC", "GHICIA"]],
lookup = vector.reduce((r,a) => {
r[a[0]] = r[a[0]] || [];
r[a[0]].push(a);
return r;
}, {});
var current = Object.keys(lookup).sort()[0];
var sorted = [];
while(Object.keys(lookup).length > 0) {
if(lookup[current] && lookup[current].length) {
var first = lookup[current].shift();
sorted.push(first);
current = first[1];
} else {
delete lookup[current];
current = Object.keys(lookup).sort()[0];
}
}
console.log(sorted);

Categories