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}`);
});
});
Related
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)
I have an empty array which i try to add unique objects (checkedAccounts) in a for loop. I log the unique objects in a console so i can confirm they are all unique. funny thing is, after the loop my array repeats the same value of the last index.
So in a case of adding [1,2,3] to the empty array inside a for loop,
instead of getting [1,2,3] i get [3,3,3]
find below my 2 different approaches that didnt work
//Approach 1
let finalAccounts:any[] = [];
let item:any = this.productModel;
let i:number = 0;
for(i = 0; i < checkedAccounts.length; i++){
item.accountNo = checkedAccounts[i].accountNo;
item.accountName = checkedAccounts[i].accountName;
item.accountType = checkedAccounts[i].accountType;
finalAccounts[i] = item;
console.log('item in loop ' + i, item);
console.log('Final acounts in loop ' + i, finalAccounts);
}
console.log('Final Accounts', finalAccounts);
//Approach 2
let finalAccounts:any[] = [];
let item:any = this.productModel;
for(let account of checkedAccounts){
temp.accountNo = account.accountNo;
temp.accountName = account.accountName;
temp.accountType = account.accountType;
finalAccounts.push(temp);
console.log('temp'+checkedAccounts.indexOf(account),temp);
}
You need to ensure that you have a fresh copy of the item in each loop iteration. Using the syntax below, we can create a shallow copy. Try this:
for(i = 0; i < checkedAccounts.length; i++){
let item:any = {...this.productModel};
item.accountNo = checkedAccounts[i].accountNo;
finalAccounts.push(item);
}
you can't share instance inside foreach while you push to a array i.e make it independent.
couple of observations
1) If you want to use any(strictly avoid, use it only if you are not sure about type) then there is no point using let item:any = {...this.productModel} you can simply achive same using let item:any = {}
2) preferable way will be typeScript way using interfaces
Code:
finalAccounts: IntTest[] = [];
export interface productModel {
accountNo: string;
address?: string;
}
for (i = 0; i < checkedAccounts.length; i++) {
let item: IntTest = {
accountNo: checkedAccounts[i].accountNo
};
finalAccounts.push(item);
}
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);
I'm trying create a function that will iterate an object of arrays, and return one array that concatenates each element from one array to each element in the other arrays:
Object like so:
kitchen = {
food: [".bacon",".bananas"],
drinks: [".soda",".beer"],
apps: ['.fritters','.wings']
}
Desired returned array:
[
".bacon.soda",".bacon.beer",
".bananas.soda",".bananas.beer",
".bacon.fritters",".bacon.wings",
".bananas.fritters",".bananas.wings",
".soda.fritters",".soda.wings",
".beer.fritters",".beer.wings"
]
I'm having difficulty wrapping my brain around how to accomplish this. One thought I had was to create another object and create a hash where each array item becomes a property and then looping through so I have something like:
newObj = {
".bacon": [".soda",".beer",".fritters",".wings"]
".bananas": [".soda",".beer"...etc]
etc...
}
then loop through each prop, concatenating the property on each array element into a new array? Not sure if that's overkill though?
Plain JS is fine, but if you have a coffeescript solution as well that would be great too.
Thanks
Here's a solution that makes use of CoffeeScript syntax (since you asked for a CoffeeScript answer and then deleted that request?):
kitchen =
food: [".bacon",".bananas"]
drinks: [".soda",".beer"]
apps: ['.fritters','.wings']
allGroups = Object.keys(kitchen).map (key) -> kitchen[key]
allValues = []
allGroups.forEach (group, i) ->
otherValues = Array.prototype.concat.apply [], allGroups.slice(i + 1)
group.forEach (v1) -> otherValues.forEach (v2) -> allValues.push(v1 + v2)
console.log(allValues)
Here is the plain JS version:
var kitchen = {
food: [".bacon", ".bananas"],
drinks: [".soda", ".beer"],
apps: ['.fritters', '.wings']
}
var allGroups = Object.keys(kitchen).map(function(key) {
return kitchen[key];
});
var allValues = []
allGroups.forEach(function(group, i) {
var otherValues = Array.prototype.concat.apply([], allGroups.slice(i + 1));
group.forEach(function(v1) {
otherValues.forEach(function(v2) {
allValues.push(v1 + v2);
});
});
});
console.log(allValues)
Try this:
var result = [];
var keys = Object.keys(kitchen);
for (var i = 0; i < keys.length; i++) {
kitchen[keys[i]].forEach(function(ingred1) {
for (var j = i+1; j < keys.length; j++) {
kitchen[keys[j]].forEach(function(ingred2) {
result.push(ingred1 + ingred2);
});
}
});
}
console.log(result);
I have an object that is being returned from a database like this: [{id:1},{id:2},{id:3}]. I have another array which specified the order the first array should be sorted in, like this: [2,3,1].
I'm looking for a method or algorithm that can take in these two arrays and return [{id:2},{id:3},{id:1}]. Ideally it should be sort of efficient and not n squared.
If you want linear time, first build a hashtable from the first array and then pick items in order by looping the second one:
data = [{id:5},{id:2},{id:9}]
order = [9,5,2]
hash = {}
data.forEach(function(x) { hash[x.id] = x })
sorted = order.map(function(x) { return hash[x] })
document.write(JSON.stringify(sorted))
function sortArrayByOrderArray(arr, orderArray) {
return arr.sort(function(e1, e2) {
return orderArray.indexOf(e1.id) - orderArray.indexOf(e2.id);
});
}
console.log(sortArrayByOrderArray([{id:1},{id:2},{id:3}], [2,3,1]));
In your example, the objects are initially sorted by id, which makes the task pretty easy. But if this is not true in general, you can still sort the objects in linear time according to your array of id values.
The idea is to first make an index that maps each id value to its position, and then to insert each object in the desired position by looking up its id value in the index. This requires iterating over two arrays of length n, resulting in an overall runtime of O(n), or linear time. There is no asymptotically faster runtime because it takes linear time just to read the input array.
function objectsSortedBy(objects, keyName, sortedKeys) {
var n = objects.length,
index = new Array(n);
for (var i = 0; i < n; ++i) { // Get the position of each sorted key.
index[sortedKeys[i]] = i;
}
var sorted = new Array(n);
for (var i = 0; i < n; ++i) { // Look up each object key in the index.
sorted[index[objects[i][keyName]]] = objects[i];
}
return sorted;
}
var objects = [{id: 'Tweety', animal: 'bird'},
{id: 'Mickey', animal: 'mouse'},
{id: 'Sylvester', animal: 'cat'}],
sortedIds = ['Tweety', 'Mickey', 'Sylvester'];
var sortedObjects = objectsSortedBy(objects, 'id', sortedIds);
// Check the result.
for (var i = 0; i < sortedObjects.length; ++i) {
document.write('id: '+sortedObjects[i].id+', animal: '+sortedObjects[i].animal+'<br />');
}
To my understanding, sorting is not necessary; at least in your example, the desired resulting array can be generated in linear time as follows.
var Result;
for ( var i = 0; i < Input.length; i++ )
{
Result[i] = Input[Order[i]-1];
}
Here Result is the desired output, Input is your first array and Order the array containing the desired positions.
var objArray = [{id:1},{id:2},{id:3}];
var sortOrder = [2,3,1];
var newObjArray = [];
for (i in sortOrder) {
newObjArray.push(objArray[(sortOrder[i]) - 1])
};
Why not just create new array and push the value from second array in?? Correct me if i wrong
array1 = [];
array2 = [2,3,1];
for ( var i = 0; i < array2 .length; i++ )
{
array1.push({
id : array2[i]
})
}