Determine next map by mapsOrder array - javascript

I have mapsOrder array and mapsData array of objects:
let mapsOrder = [1,2,1,3];
let mapData = [
{
id: 1,
gates: [
{
toId: 2,
coords: {
x: 2,
y: 42
}
},
{
toId: 3,
coords: {
x: 9,
y: 4
}
}
]
},
{
id: 2,
gates: [
{
toId: 1,
coords: {
x: 6,
y: 5
}
}
]
},
{
id: 3,
gates: [
{
toId: 1,
coords: {
x: 2,
y: 1
}
}
]
}
]
What I want to achieve is in loop basing on mapsOrder where mapsOrder array values are ids in mapData, designate gates to next map.
So we have loop that iterate 4 times and when:
loop index is 1 current map is 1 next map is 2 and gates to next are coords: { x: 2, y: 42 }
loop index is 2 current map is 2 next map is 1 and gates to next are coords: { x: 6, y: 5 }
loop index is 3 current map is 1 next map is 3 and gates to next are coords: { x: 9, y: 4 }
loop index is 4 current map is 3 next map is 1 and gates to next are coords: { x: 2, y: 1 }
last loop iteration see next map as first of mapsOrder array. I tried to do it myself by first determineting the id of next map like so:
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapData[mapsOrder[i] - 1].id;
if(i === mapsOrder.length - 1) {
nextMap = mapData[0].id
} else {
nextMapId = mapData[mapsOrder[i]].id;
}
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId)
console.log('break-----')
}
but this console incorrect ids, demo

If you don't care about the original array then just use shift to get the next gate (shift will remove the gate from the array thus the next gate will be available when the object is encountered again). Use find to find the object from the array:
let result = mapsOrder.map(id =>
mapData.find(o => o.id == id).gates.shift().coords
);
You may want to check if find actually finds something and the gates array contains something before using shift, here is a safer way:
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
if(obj && obj.gates.length) { // if we found an object with the same id and that object still have gates
return obj.gates.shift().coords; // return the coords of the first gate and remove the gate from the array
} // otherwise, throw an error or something
});
No altering:
Instead of using shift from the previous example, we'll just use an object to track the gate index from the gates array:
let nextGateIndex = Object.create(null); // create a prototypeless object to track the next gate index for each object
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
let index;
if(nextGateIndex[id] == undefined) {
index = 0;
} else {
index = nextGateIndex[id] + 1;
}
nextGateIndex[id] = index;
if(obj && index < obj.gates.length) {
return obj.gates[index].coords;
} // throw error or something
});

If follow your description your loop should look like. Seems that you wand to use id and toId but using array indexes. It can be a good idea to replace arrays with objects.
Demo
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapsOrder[i];
if(i === mapsOrder.length - 1) {
nextMapId = mapsOrder[0]
} else {
nextMapId = mapsOrder[i + 1];
}
let filteredMapData = mapData.filter(f => f.id == currentMapId);
let filteredGates = filteredMapData.length > 0 ? filteredMapData[0].gates.filter(f => f.toId == nextMapId) : [];
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId, 'gates:', filteredGates.length == 0 ? "no gates": filteredGates[0].coords)
console.log('break----')
}

I would recommend the filter() function for javascript arrays as it is super quick. This function will return an array filled with items from original matching some criteria (in this case, objects having the desired id).
for (let i = 0; i < mapsOrder.length; i++) {
console.log(mapData.filter(mapDataItem => mapDataItem.id === mapsOrder[i]))
}

Related

JS Create array of objects containing random unique numbers

In javascript I want to create an array of 20 objects containing 2 random numbers between 1 and 250. All numbers in the array I want to be unique from each other. Basically like this:
const matches = [
{ player1: 1, player2: 2 },
{ player1: 3, player2: 4 },
{ player1: 5, player2: 6 },
{ player1: 7, player2: 8 },
...
]
// all unique numbers
I have found this other method
const indexes = [];
while (indexes.length <= 8) {
const index = Math.floor(Math.random() * 249) + 1;
if (indexes.indexOf(index) === -1) indexes.push(index);
}
But this only returns an array of numbers:
[1, 2, 3, 4, 5, 6, 7, 8, ...]
You could use Array.from method to create an array of objects and then also create custom function that will use while loop and Set to generate random numbers.
const set = new Set()
function getRandom() {
let result = null;
while (!result) {
let n = parseInt(Math.random() * 250)
if (set.has(n)) continue
else set.add(result = n)
}
return result
}
const result = Array.from(Array(20), () => ({
player1: getRandom(),
player2: getRandom()
}))
console.log(result)
You can create an array of 251 elements (0-250) and preset all values to 0 to keep track of the generated elements. Once a value is generated, you mark that value in the array as 1.
Check below:
// create an array of 251 elements (0-250) and set the values to 0
let array = Array.from({ length: 251 }, () => 0);
let matches = [];
function getRandomUniqueInt() {
// generate untill we find a value which hasn't already been generated
do {
var num = Math.floor(Math.random() * 249) + 1;
} while(array[num] !== 0);
// mark the value as generated
array[num] = 1;
return num;
}
while (matches.length <= 4) {
let obj = { "player1" : getRandomUniqueInt(), "player2" : getRandomUniqueInt() };
matches.push(obj);
}
console.log(matches);

How to get lastindexof an array using key value pair [duplicate]

This question already has answers here:
indexOf method in an object array?
(29 answers)
Closed 3 years ago.
I am trying to get the last index of a value in an array of objects.
I am unable to make it work; I am expecting the lastIndexOf an element id with value 0.
var sample = [
{
id: 0,
name: 'abbay',
rank: 120
},
{
id: 1,
name: 'sally',
rank: 12
},
{
id: 0,
name: 'abbay',
rank: 129
}
];
var index = this.sample.lastIndexOf(0{id});
Argument of type '0' is not assignable to parameter of type '{id: number; name: string; rank: number;}'.
You can map into an array of booleans:
var lastIndex =sample.map(s =>
s.id === 0).lastIndexOf(true);
then access your array by last index:
console.log(sample[lastIndex]);
Array's lastIndexOf method compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator). If your array contains objects, then you have to use another method.
If performance is not important and the amount of data is not that big, you can use
const lastIndex = sample.length - 1 - sample
.slice()
.reverse()
.findIndex( item => item.id === 0 );
slice will create a copy of the array, reverse will reverse it, findIndex will return the first item that matches o.id === 0 and the final result is subtracted from sample.length - 1. It's not very efficient for a large data set.
Or you can use a plain for
function findLastIndexOf(arr) {
for (let i = arr.length; i--;) {
if (arr[i].id === 0) {
return i;
}
}
}
findLastIndexOf(sample);
for (let i = arr.length; i--;) looks weird but it will start iterating from the last position and stop when i reach the value of 0. Give it a try.
Hope it helps
Try this:
const lastIndex = sample.map(res=>res.id).lastIndexOf(0) // id = 0
console.log(lastIndex) // 2
const lastIndexWithIdZero = this.sample.length - this.sample.reverse().findIndex(i => i.id === 0);
if (lastIndexWithIdZero > arrLen) {
throw new Error('didn\'t worked');
}
forget that, it's slow, better use just
let lastIndexWithIdZero = -1;
for (let i = 0, v; v = sample[i]; i++) {
if (v.id === 0) {
lastIndexWithIdZero = i;
}
}
console.log(lastIndexWithIdZero);
http://jsben.ch/LY1Q0
You could filter the results, then reverse the results and grab the first item.
const sample = [{
id: 0,
name: "abbay",
rank: 120
},
{
id: 1,
name: "sally",
rank: 12
},
{
id: 0,
name: "abbay",
rank: 129
}
]
console.log(
sample
// Add the index to the object
.map((i, idx) => ({id: i.id, idx}))
// Filter the object where id == 0
.filter(i => i.id == 0)
// Reverse the result and get the first item
// Get the idx
.reverse()[0].idx
)

How to limit for 10 results the array.filter?

I have big array, I want to make an autocomplete search, but I want to display only 10 results, so stop iterating through the array by the time there were found 10 results. I have made this:
let items = array.filter(r => r.indexOf(term)!=-1);
console.log(items.length) // lots of items, need to be limited to 10
It works but I don't know how to stop the array.filter by the time it reaches the desired limit.
You could use another variable to keep track of how many items matched the condition so far and always return false after the limit has been reached. Here is an example:
const arr = [1,0,2,0,3,0,4,5,6,7,8,9,10,11,12,13,14];
const filtered = arr.filter(function(item) {
if (this.count < 10 && item > 0) {
this.count++;
return true;
}
return false;
}, {count: 0});
console.log(filtered);
Here, I'm using an object {count: 0} as the context of the callback function. You can find out more about Array.filter from here
Basically you can use a generator function, which can be stopped by a self made limit, like in the below function
function *filter(array, condition, maxSize) {
if (!maxSize || maxSize > array.length) {
maxSize = array.length;
}
let count = 0;
let i = 0;
while ( count< maxSize && i < array.length ) {
if (condition(array[i])) {
yield array[i];
count++;
}
i++;
}
}
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log( Array.from( filter(array, i => i % 2 === 0, 2 ) ) ); // expect 2 & 4
So it will stop after it reaches maxSize as a parameter, and to easily return it into an array, you can use Array.from, which will iterate the iterator of the generator function
You could hand over a counter and omit any other values for filtering.
const
filter = v => v % 2,
filterMax = (fn, c) => x => c && fn(x) && c--,
max = 3,
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
result = array.filter(filterMax(filter, max));
console.log(result);
Taking the idea of Icepickle's answer a bit ahead with a loop for finding the next valid item and yield this one.
function* filterMax(array, cb, count) {
var i = 0;
while (count) {
while (i < array.length && !cb(array[i])) i++;
if (i >= array.length) return;
yield array[i++];
count--;
}
}
const
filter = v => v % 2,
max = 3,
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(...filterMax(array, filter, max));
You can't break from Array.prototype.filter method. It will loop over every element. You can use a simple for loop and break when 10 items are found
const items = []
for (const value of array) {
if (value.includes(term))
items.push(value)
if (items.length === 10)
break;
}
Just for the trick :
EDIT : To clarify this code will pick the 10 first even number of the list
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
const result = array.reduce((temp, value) => {
if(value%2==0 && temp.length<10)
temp.push(value);
return temp;
}, []);
console.log(result);
var data = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14"]
var limited = data.filter((val,i)=>i<10)
console.log(limited)
You can do this just simple add .Slice(0,NO_OF_ELE_WANT)
eg. finding first two even no
[1,2,3,4,5,6,7,8,9,10].filter((e)=> e%2==0).slice(0,2)
Answer : let items = array.filter(r => r.indexOf(term)!=-1).slice(0,10);
I wrote a library that's handy for this sort of thing.
Here's how I'd find the first 100 numbers that start with the character "1"
const {blinq, range} = window.blinq;
//create a large array of strings to search
const arrToBeSearched = range(0,10000)
.select(x => `${x}`)
.toArray()
const query = blinq(arrToBeSearched)
.where(x => x.startsWith("1"))
.takeWhile((x, i) => i < 100)
const result = [...query] //no calculation until we materialize on this line
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/blinq"></script>
I know its a bit late, but here's for the new comers!
// we'll create a function which will take two arguments
// first argument would be your original array which your want to filter from
// second argument would be the number of results you want the filter to return
const limitedArray = (originalArray, limit) => {
let newArray = [];
for (let item of originalArray) {
if (newArray.length >= limit) break;
//your code here
//in my case i'll jush push in to the array
newArray.push(item)
}
return newArray;
};
//---------------->ignore v<-------------------
//the above function would return an array so in other words we can see this function as an array
const array = [1, 2, 3, 4, 5, 6, 'cascas', 'ascasc', 9, 10, 'ascs'];
console.log(limitedArray(array, 4));
//similarly
limitedArray(array, 4).forEach(item => {
console.log(item)
})
You can define your custom method on Array.prototype which will take 2 arguments. A callback and a max elements that result array will contain.
The code below gets the first 3 odd numbers from array.
function filterUpto(callback,max){
let len = this.length
let res = [];
let i = 0;
while(res.length < max && i < len){
if(callback(this[i],i,this)) res.push(arr[i])
i++
}
return res;
}
Object.defineProperty(Array.prototype,'filterUpto',{
value:filterUpto
})
let arr = [1,2,3,4,5,6,7,8,9,10];
console.log(arr.filterUpto(x => x % 2,3)); //first three odd numbers
Here is another possible solution, pretty straightforward, using Array.from:
const arr = [
"foo",
"bar",
"foobar",
"baz",
"foobaz",
"artefact",
"mortar",
"bar",
"arity",
"mark",
"car",
"dare",
"arbitrary",
"tar",
"jar",
"war",
];
const filterWithLimit = (arr, value, length) =>
Array.from(
{ length },
function () {
return arr
.slice(this.index++)
.find((option, i) => (this.index += i, option.includes(value)));
},
{ index: 0 }
);
console.log(filterWithLimit(arr, "ar", 10));
Here is a short solution which doesn't continue searching after the limit is reached:
function filter_n(array, limit, test) {
let ret = []
array.find((x)=> test(x) && ret.push(x)>=limit )
return ret
}
when test(x) is true, it calls ret.push(x) (which adds x to ret and outputs the length of ret)
then, once ret's length is >= limit, the inner function returns true, and find stops looping because it "found" a result

Javascript smart filtering array of objects

i have an array of objects that has a key , value pair like below .
players=[
{
id : 1,
name : "player1",
value : 5.6,
position : "Goalkeeper"
},{
id : 1,
name : "player1",
value : 7.7,
position : "Defender"
},{
id : 1,
name : "player2",
value : 6.1,
position : "Midfielder"
},{
id : 1,
name : "player1",
value : 7.2,
position : "Forward"
},.....n ]
What i want to achieve is autoselect 15 players where goalkeepers should be 2 , 5 defenders , 5 midfielders and 3 forwards from array of 700 players so that their total value is close to or equal to 100 . Any help would be appreciated :-)
At first seperate the players into their positions:
const getPosition = (arr, pos) => arr.filter(({position}) => position === pos);
Then use a method to combine these arrays into an array with a specified length that trys out all combinations:
function combinations(arr, length) {
function* walk(start, depth) {
for(let i = start; i < arr.length; i++) {
if(depth) {
for(const combo of walk(i + 1, depth - 1)) {
yield [...combo, arr[i]];
}
} else {
yield [arr[i]];
}
}
}
return walk(0, length);
}
Now to get all different teams we can combine them like this:
function* compose(iterator, ...iterators) {
for(const value of iterator) {
if(iterators.length) {
for(const combo of compose(...iterators)) {
yield [value, ...combo];
}
} else {
yield [value];
}
}
}
const teams = compose(
combinations(getPosition("Goalkeeper"), 3),
combinations(getPosition("Defender"), 5),
combinations(getPosition("Midfielder"), 5),
combinations(getPosition("Forward"), 3)
);
Now we just have to find the best team:
const distance = team => Math.abs(team.reduce((score, player) => score + player.value, 0) - 100);
let best = teams.next().value;
for(const team of teams) {
if(distance(best) > distance(team))
best = team;
if(!distance(best)) break;
}

find intersection elements in arrays in an array

I need to construct a function intersection that compares input arrays and returns a new array with elements found in all of the inputs.
The following solution works if in each array the numbers only repeat once, otherwise it breaks. Also, I don't know how to simplify and not use messy for loops:
function intersection(arrayOfArrays) {
let joinedArray = [];
let reducedArray = [];
for (let iOuter in arrayOfArrays) {
for (let iInner in arrayOfArrays[iOuter]) {
joinedArray.push(arrayOfArrays[iOuter][iInner]);
}
return joinedArray;
}
for (let i in joinedArray.sort()) {
if (joinedArray[i] === joinedArray[ i - (arrayOfArrays.length - 1)]) {
reducedArray.push(joinedArray[i]);
}
}
return reducedArray;
}
Try thhis:-
function a1(ar,ar1){
x = new Set(ar)
y = new Set(ar1)
var result = []
for (let i of x){
if (y.has(i)){
result.push(i)
}
}
if (result){return result}
else{ return 0}
}
var a= [3,4,5,6]
var b = [8,5,6,1]
console.log(a1(a,b)) //output=> [5,6]
Hopefully this snippet will be useful
var a = [2, 3, 9];
var b = [2, 8, 9, 4, 1];
var c = [3, 4, 5, 1, 2, 1, 9];
var d = [1, 2]
function intersect() {
// create an empty array to store any input array,All the comparasion
// will be done against this one
var initialArray = [];
// Convert all the arguments object to array
// there can be n number of supplied input array
// sorting the array by it's length. the shortest array
//will have at least all the elements
var x = Array.prototype.slice.call(arguments).sort(function(a, b) {
return a.length - b.length
});
initialArray = x[0];
// loop over remaining array
for (var i = 1; i < x.length; i++) {
var tempArray = x[i];
// now check if every element of the initial array is present
// in rest of the arrays
initialArray.forEach(function(item, index) {
// if there is some element which is present in intial arrat but not in current array
// remove that eleemnt.
//because intersection requires element to present in all arrays
if (x[i].indexOf(item) === -1) {
initialArray.splice(index, 1)
}
})
}
return initialArray;
}
console.log(intersect(a, b, c, d))
There is a nice way of doing it using reduce to intersect through your array of arrays and then filter to make remaining values unique.
function intersection(arrayOfArrays) {
return arrayOfArrays
.reduce((acc,array,index) => { // Intersect arrays
if (index === 0)
return array;
return array.filter((value) => acc.includes(value));
}, [])
.filter((value, index, self) => self.indexOf(value) === index) // Make values unique
;
}
You can iterate through each array and count the frequency of occurrence of the number in an object where the key is the number in the array and its property being the array of occurrence in an array. Using the generated object find out the lowest frequency of each number and check if its value is more than zero and add that number to the result.
function intersection(arrayOfArrays) {
const frequency = arrayOfArrays.reduce((r, a, i) => {
a.forEach(v => {
if(!(v in r))
r[v] = Array.from({length:arrayOfArrays.length}).fill(0);
r[v][i] = r[v][i] + 1;
});
return r;
}, {});
return Object.keys(frequency).reduce((r,k) => {
const minCount = Math.min(...frequency[k]);
if(minCount) {
r = r.concat(Array.from({length: minCount}).fill(+k));
}
return r;
}, []);
}
console.log(intersection([[2,3, 45, 45, 5],[4,5,45, 45, 45, 6,7], [3, 7, 5,45, 45, 45, 45,7]]))

Categories

Resources