1 - I need to stop the loop and return an error if the 'grid_id' is not found in the variationsDB array
2 - If found, it takes the 'variation_id' and checks if it exists in the variations array
In this second case, I also need to stop the loop and return an error if the 'variation_id' is not found in the variations array
const grids = [{ "grid_id": 1 }, { "grid_id": 2 }]
const variationsDB = [
{
"variation_id": 3,
"grid_id": 1
},
{
"variation_id": 7,
"grid_id": 2
}
]
const variations = [{ "variation_id": 3 }, { "variation_id": 7 }]
const filter = (a, b, key) => {
const array = []
a.forEach((x) => {
b.forEach((y) => {
if (x[key] === y[key]) {
array.push(y)
}
})
})
return array
}
const filterA = filter(grids, variationsDB, 'grid_id')
const filterB = filter(filterA, variations, 'variation_id')
console.log(filterB)
// [{ "variation_id": 3 }, { "variation_id": 7 }]
Just checking if the resulting array is empty or not sounds like it'd be enough:
const filterA = filter(grids, variationsDB, 'grid_id')
if (!filterA.length) {
throw new Error('No matches found for grid_id');
}
const filterB = filter(filterA, variations, 'variation_id')
if (!filterB.length) {
throw new Error('No matches found for variation_id');
}
console.log(filterB)
Your code's computational complexity can be reduced from O(n ^ 2) to O(n) by organizing the first array of items into a Set of values first:
const grids = [{ "grid_id": 1 }, { "grid_id": 2 }]
const variationsDB = [
{
"variation_id": 3,
"grid_id": 1
},
{
"variation_id": 7,
"grid_id": 2
}
]
const variations = [{ "variation_id": 3 }, { "variation_id": 7 }]
const validate = (a, b, key) => {
const aValues = new Set(a.map(aItem => aItem[key]));
for (const bItem of b) {
if (!aValues.has(bItem[key])) {
throw new Error('No match found for', key, bItem[key]);
}
}
}
validate(grids, variationsDB, 'grid_id')
validate(variationsDB, variations, 'variation_id')
console.log(variations)
Related
I made this function to generate custom fixture, but i have an issue if the players in rounds are more than 8. I tried to add 10 playes instead of 8, and the loop return me a wrong result.
let teams = [
{ id: "Team1" },
{ id: "Team2" },
{ id: "Team3" },
{ id: "Team4" },
{ id: "Team5" },
{ id: "Team6" },
{ id: "Team7" },
{ id: "Team8" },
{id: "Team9" },
{id: "Team10" }
]
// Total rounds
let totRounds = 3
// Array with rounds
let rounds = []
for (let i = 0; i < totRounds; i++) {
// This loop add an array on enemyes teams enemies["team2,..."]
teams.forEach(team => team.enemies = teams.filter(enemy => enemy !== team));
// Empty Array for first round
const matches = [];
// Empty Array for second round ( return )
const matches_return = [];
while (teams.some(team => team.enemies.length)) {
const playing = [];
const playing_return = []
for (const team of teams) {
//debugger
if (playing.includes(team)) continue;
const enemy = team.enemies.find(enemy => !playing.includes(enemy));
if (!enemy) continue;
team.enemies.splice(team.enemies.indexOf(enemy), 1);
enemy.enemies.splice(enemy.enemies.indexOf(team), 1);
playing.push(team, enemy);
playing_return.push(enemy, team);
}
if (playing.length) matches.push(playing.map(t => t.id))
if (playing_return.length) matches_return.push(playing_return.map(t => t.id))
}
// Merge 2 arrays
const totalMatches = matches.concat(matches_return)
rounds.push(totalMatches);
}
console.log(rounds);
This work with 8 players, but with 10 or more not. I tried to made some changes but they didn't work.
So I have an array of objects that contain information for different activities in different projects.
It looks something like this
const input = [
{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
From this, I would like to go to this
output = [{
Project: 1,
ID1: 1 - 1,
ID2: 1 - 2,
},
{
Project: 1,
ID1: 2 - 1,
},
];
Here's what I have so far:
let currentProject = ''
let output = []
for (const e of input) {
let counter
let outputObj = {}
if (currentProject !== e.Project) {
output.push(outputObj)
counter = 1
outputObj = {}
outputObj.projectNum = e.Project
currentProject = e.Project
}
if (currentProject == e.Project) {
outputObj['ID' + counter] = e.ID
counter++
}
}
here's what I'm getting back:
output = [{
Project: 1,
ID1: 1 - 1
},
{
Project: 1,
ID1: 2 - 1
}
]
I'm not sure what the issue is, tried several times to fix it.
Could someone please help me get over the edge?
Any help will be greatly appreciated.
You can achieve this using reduce, Object.keys
const input = [{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
const result = input.reduce((acc, curr) => {
const { Project, ID } = curr;
const obj = acc.find((el) => el.Project === Project);
if (obj) {
const length = Object.keys(obj).length;
obj[`ID${length}`] = ID;
} else {
acc.push({ Project, [`ID${1}`]: ID });
}
return acc;
}, []);
console.log(result);
You can try this.
const input=[{Project:1,ID:"1-1",},{Project:1,ID:"1-2",},{Project:2,ID:"2-1"}];
let temp = {};
input.map(v=>(temp[v.Project] ??= []).push(v.ID));
let output = Object.keys(temp).map(k=>{
let json = {Project:k};
temp[k].map((v,k)=>json['ID'+(Number(k+1))]=v);
return json;
});
console.log(output);
you will get the result.
[
{ Project: '1', ID1: '1-1', ID2: '1-2' },
{ Project: '2', ID1: '2-1' }
]
The way you intended to implement this assumes every project from same id comes sequentially grouped.
While #decpk answer deals with with a linear search, for performance reasons I would rather first use a dictionary and then convert to an array, AND also keep track of id quantities using a field n.
const input = [
{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
const projects = {}
for (const e of input) {
let pid = e.Project
let project = projects[pid]
//First time seeing this project
if (!project) {
projects[pid] = { Project: pid, n: 1, ID1: e.ID }
}
//insert more ID
else {
project.n += 1
project[`ID${project.n}`] = e.ID
}
}
//And now converting the object to array, removing the 'n' field
const output = Object.keys(projects).map(pid => {
const obj = projects[pid]
delete obj.n
obj.Project = pid
return obj
})
You can try this way - O(n) time complexity
Using reduce to aggregate data.
Using logical nullish assignment only assigns if acc[Project] is nullish (null or undefined).
Define each additional key-value pair like:
const number = Object.keys(acc[Project]).length; // Define key format by number of existing property.
const key = `ID${number}`;
instead of using count variable.
const input=[{Project:1,ID:"1-1",},{Project:1,ID:"1-2",},{Project:2,ID:"2-1",}];
const output = input.reduce((acc, {Project, ID}) =>
{
acc[Project] ??= {Project}; // Get exist object or create new one
const number = Object.keys(acc[Project]).length; // Define key format by number of existing property.
const key = `ID${number}`;
acc[Project][key] = ID;
return acc;
}, {});
console.log(Object.values(output));
Output:
[
{
"Project": 1,
"ID1": "1-1",
"ID2": "1-2"
},
{
"Project": 2,
"ID1": "2-1"
}
]
Good morning, after an array.map I have an array containing the same assignments with some nested ratings:
const assignments = [
{
name: "assignmentOne",
difficultyRating: 1,
funRating: 2
},
{
name: "assignmentOne",
difficultyRating: 3,
funRating: 4
},
{
name: "assignmentOne",
difficultyRating: 5,
funRating: 1
}
]
Now I would like to get the total difficulty/fun rating, which would look like one of the following:
//Both the difficulty and fun rating in the same record
const assignmentsTotal = [
{
name: "assignmentOne",
totalDifficultyRating: 9,
totalFunRating: 7
}
]
//Difficulty and fun rating as separate records
const assignmentsDifficultyTotal = [
{
name: "assignmentOne",
totalDifficultyRating: 9
}
]
const assignmentsFunTotal = [
{
name: "assignmentOne",
totalFunRating: 7
}
]
I'm pretty confident the best way to do this is using the reduce method.
After some digging around the only thing that came close to what I want to achieve is the following article, yet I'm not able to get this to work properly. Is there a good way to do this from the starting point above, or would it be better to create separate arrays using array.map and after use the reduce method?
If you are looking for same 'name' objects in array, below should be ok:
const reducer = assignments.reduce((total, current) => {
return { name: current.name, difficultyRating : total.difficultyRating + current.difficultyRating, funRating : total.funRating + current.funRating } });
if you want to group objects by name, then look at lodash groupby function. In general, lodash is very handy in all array/obj functionalities.
const assignments = [{
name: "assignmentOne",
difficultyRating: 1,
funRating: 2
},
{
name: "assignmentOne",
difficultyRating: 3,
funRating: 4
},
{
name: "assignmentOne",
difficultyRating: 5,
funRating: 1
},
{
name: "assignmentTwo",
difficultyRating: 5,
funRating: 3
},
{
name: "assignmentTwo",
difficultyRating: 5,
funRating: 1
}
];
// if you want the totals as an array:
const assignmentsTotalArray = assignments.reduce((totalArr, item) => {
// check whether the assignment is already in the array
const assignmentIndex = totalArr.findIndex(elem => elem.name === item.name);
// if the assignment is not in the array, add it and initialize the totals
// otherwise update the totals
if (assignmentIndex === -1) {
totalArr.push({
name: item.name,
totalDifficultyRating: item.difficultyRating,
totalFunRating: item.funRating
});
} else {
totalArr[assignmentIndex].totalDifficultyRating += item.difficultyRating;
totalArr[assignmentIndex].totalFunRating += item.funRating;
}
return totalArr;
}, []);
console.log('### assignmentsTotalArray:');
console.log(assignmentsTotalArray);
// if you want the totals as an object:
const assignmentsTotalObject = assignments.reduce((totalObj, item) => {
// if the output object already contains the assignment, sum the ratings
// otherwise create a new key for the assignment and initialize the ratings
if (totalObj[item.name]) {
totalObj[item.name].totalDifficultyRating += item.difficultyRating;
totalObj[item.name].totalFunRating += item.funRating;
} else {
totalObj[item.name] = {
totalDifficultyRating: item.difficultyRating,
totalFunRating: item.funRating
};
}
return totalObj;
}, {});
console.log('### assignmentsTotalObject:')
console.log(assignmentsTotalObject);
I was trying to sort an array like this by "walls":
[{
"536606220753305611": { "walls": 4 },
"137616812886982656": { "walls": 5 },
"189862971520843776": { "walls": 4 },
"230433188491558913": { "walls": 2 }
}]
I can't seem to access the array from within the ids with array.sort.
Here's what I've tried:
let top10 = array.sort(function(a,b){
return a.walls.localeCompare(b.walls).slice(0, 10);
});
It won't work here because I can't find how to get inside the id arrays.
I expect the result I logged to be in order, but it shows this instead:
[ { '536606220753305611': { walls: 4 },
'137616812886982656': { walls: 5 },
'189862971520843776': { walls: 4 },
'230433188491558913': { walls: 2 } } ]
Thanks!
Codingpro
If the desired output is an object with ordered keys, that can't be done (JS objects' properties are inherently unordered). You can first transform the input into an array of objects, then sort those....
let data = [{
"536606220753305611": { "walls": 4 },
"137616812886982656": { "walls": 5 },
"189862971520843776": { "walls": 4 },
"230433188491558913": { "walls": 2 }
}]
// transform the input object into an array of objects of the form:
//
// [ { "long string of digits" : { walls : 4 } },
// { "long string of digits" : { walls : 2 } },
// etc.
// ]
let data0 = data[0];
let objects = Object.keys(data0).map(k => {
return { k: data0[k] }
});
let sorted = objects.sort((a, b) => {
let keyA = Object.keys(a)[0];
let keyB = Object.keys(b)[0];
return a[keyA].walls - b[keyB].walls;
});
console.log(sorted)
What you're trying to do isn't possible with your current data structure.
You basically have one element in your array that you're trying to sort... which by default is already sorted. And it isn't possible to sort the keys in an object as they do not have any specific order.
For your example to work, you need to create multiple elements in your your list to sort. Here is an example of what you could do.
const data = [{
"536606220753305611": {
"walls": 4
}
}, {
"137616812886982656": {
"walls": 5
}
}, {
"189862971520843776": {
"walls": 4
}
}, {
"230433188491558913": {
"walls": 2
}
}];
const res = data.sort((a,b)=>{
return Object.values(a).shift().walls - Object.values(b).shift().walls;
});
console.log(res);
Here you have another approach that use the experimental flatMap() and will also work if you have one initial array with multiple objects containing data:
const input = [
{
"536606220753305611": { "walls": 4 },
"137616812886982656": { "walls": 5 },
"189862971520843776": { "walls": 4 },
"230433188491558913": { "walls": 2 }
},
{
"336606220753305611": { "walls": 9 },
"437616812886982656": { "walls": 7 },
"789862971520843776": { "walls": 23 },
"90433188491558913": { "walls": 1 }
}
];
let res = input.flatMap(e => Object.entries(e))
.sort((a, b) => a[1].walls - b[1].walls)
.map(([key, value]) => ({[key]: value}));
console.log(res);
If you wanted to keep a similar structure but sort the keys/values you could always swap your array for a Map instead.
const data = [{
"536606220753305611": { "walls": 4 },
"137616812886982656": { "walls": 5 },
"189862971520843776": { "walls": 4 },
"230433188491558913": { "walls": 2 }
}];
// Create a new Map from your existing data
const map = new Map(Object.entries(data[0]));
// Create an array of Map entries sorted by wall value
// and then create a new Map from that array
const sortedMap = new Map([...map.entries()].sort((a, b) => {
return a[1].walls < b[1].walls;
}));
// Voila, a sorted Map
for (let [k, v] of sortedMap.entries()) {
console.log(k, v);
}
I have written a logic to merge all the Batches from message2 variable. It will merge all the Batch if there is a duplicate Batch name (AA, BB) and calculate the lines.
var message2 = {
Batches: [
{Batch: "AA", Lines: 1 },
{Batch: "BB", Lines: 2 },
{Batch: "BB", Lines: 6 }
]
}
Become:
[ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 8 } ]
This is done by reduce() method.
In the forEach loop, it loops all the mergedBatches (after merged) and compares with the batch in the Worker variable. It will need to find the same batch name if the Worker line is more then mergedBatches line then set mergedBatches to match the Worker line.
var message2 = {
Batches: [
{Batch: "AA", Lines: 1 },
{Batch: "BB", Lines: 2 },
{Batch: "BB", Lines: 6 }
]
}
var Worker = {
Batches: [
{Batch: "AA", Lines: 2 },
{Batch: "BB", Lines: 3 },
]
}
var mergedBatches = message2.Batches.reduce((acc, obj)=>{
var existObj = acc.find(b => b.Batch === obj.Batch);
if(existObj) {
existObj.Lines += obj.Lines;
return acc;
}
acc.push({Batch: obj.Batch, Lines: obj.Lines});
return acc;
},[]);
mergedBatches.forEach((b) => {
var workerBatch = Worker.Batches.find(wB => wB.Batch === b.Batch);
if (b.Lines >= workerBatch.Lines) {
b.Lines = workerBatch.Lines;
}
});
console.log(mergedBatches)
Final result which is working as expected:
[ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 3 } ]
Is there a way to refactor this code to make it readable or a better way?
Here is a shorter version:
if mergedBatches should not contain references to message2.Batches entries you can use destructuring: acc.push({ ...cur });
one-line if/else should be more readable without the brackets;
null-check in the latest condition: find can return undefined.
const message2 = {
Batches: [
{Batch: "AA", Lines: 1 },
{Batch: "BB", Lines: 2 },
{Batch: "BB", Lines: 6 }
]
}
const Worker = {
Batches: [
{Batch: "AA", Lines: 2 },
{Batch: "BB", Lines: 3 },
]
}
const mergedBatches = message2.Batches.reduce((acc, cur) => {
const prev = acc.find(x => x.Batch === cur.Batch)
if (prev) prev.Lines += cur.Lines
else acc.push(cur)
return acc
}, [])
mergedBatches.forEach((mb) => {
const wb = Worker.Batches.find(x => x.Batch === mb.Batch)
if (wb && wb.Lines < mb.Lines ) mb.Lines = wb.Lines
})
console.log(mergedBatches)
This is a bit more straight forward and should be faster:
const mergeBatches = (message) => {
const obj = {};
for (let i = message.Batches.length; i--;) {
const current = message.Batches[i];
if (current.Batch in obj) {
obj[current.Batch] += current.Lines;
} else {
obj[current.Batch] = current.Lines;
}
}
const arr = [];
for (let key in obj) {
arr.push({
Batch: key,
Lines: obj[key]
})
}
return arr;
}
Its really good that you're learning functional patterns, but they aren't always the fastest.
For example, your code you have acc.find. Underneath the hood, find is iterating over the array acc every time that function executes which makes the complexity O(n * n) I think its this, someone comment if I'm wrong.
In the function I provided, you're only iterating over the Batches array once which makes this O(n).
From your current structure this would be another way to arrive to your expecting result:
const merged = {};
message2.Batches.forEach(b => {
if(merged[b.Batch]) {
merged[b.Batch].Lines += b.Lines;
} else {
merged[b.Batch] = b;
}
});
const result = [];
Worker.Batches.forEach(b => {
if (merged[b.Batch] && merged[b.Batch].Lines > b.Lines) {
merged[b.Batch].Lines = b.Lines;
}
result.push(merged[b.Batch]);
});
// Output
[{ "Batch": "AA", "Lines": 1 }, { "Batch": "BB", "Lines": 3 }]