Generate every topological sort of a graph - javascript

Consider this input array which represents a graph:
[
{"id":1,"prev":["NaN"]},
{"id":2,"prev":["NaN"]},
{"id":3,"prev":[1]},
{"id":4,"prev":[2]},
{"id":5,"prev":[2]},
{"id":6,"prev":[3,4]},
{"id":7,"prev":[5,6]}
]
My task is to find each possible option of ordering the elements in the list.
The order of the elements depends on whether the element has a previous one or not. For example, no. 7 will always be the last since it has 2 previous elements and no followers.
I tried to implement as follows but without success:
var possibleSolutions = [];
recursiveCheck(tasks.slice(), tasks.length, []);
function recursiveCheck(array, noCalls, currentSolution) {
var solution = currentSolution;
array.forEach((task, index, object) => {
if (task.prev.length <= 1 && isNaN(task.prev[0])) {
var tmpTasks = array.slice();
solution.push(task.id);
tmpTasks.splice(index, 1);
tmpTasks.forEach(el => {
el.prev.forEach((prevEl, index, object) => {
if (prevEl == task.id) {
object.splice(index, 1)
}
})
})
noCalls--;
if (noCalls == 0) {
possibleSolutions.push(solution)
solution = [];
} else {
recursiveCheck(tmpTasks, noCalls, solution);
}
}
});
}
This should be the output:
[
[1,2,3,4,5,6,7],
[1,2,3,4,6,5,7],
[1,2,3,5,4,6,7],
[1,2,4,3,5,6,7],
[1,2,4,3,6,5,7],
[1,2,4,5,3,6,7],
[1,2,5,3,4,6,7],
[1,2,5,4,3,6,7],
[1,3,2,4,5,6,7],
[1,3,2,4,6,5,7],
[1,3,2,5,4,6,7],
[2,1,3,4,5,6,7],
[2,1,3,4,6,5,7],
[2,1,3,5,4,6,7],
[2,1,4,3,5,6,7],
[2,1,4,3,6,5,7],
[2,1,4,5,3,6,7],
[2,1,5,3,4,6,7],
[2,1,5,4,3,6,7],
[2,4,1,3,5,6,7],
[2,4,1,3,6,5,7],
[2,4,1,5,3,6,7],
[2,4,5,1,3,6,7],
[2,5,1,3,4,6,7],
[2,5,1,4,3,6,7],
[2,5,4,1,3,6,7]
]
As another example, the array cannot be arranged like [1,3,6,...] because 6 has a previous element 4 and therefore element 4 must be before 6.

Let's discuss this a bit more abstractly.
A directed graph is the data structure we're really taking as input here. We have a set V of vertices/nodes and a set E of edges. Each edge is an ordered pair (v1, v2), where v1 and v2 are both vertices, representing an arrow from v1 to v2. Here, we're representing the graph using the "adjacency list" representation.
The task is to find all the ways to topologically sort this graph.
We can describe the ways to topologically sort a graph as follows:
If we want to topologically sort the empty graph (the graph with no vertices), this is easy: the only way to do it is to output the empty sorting []. So the list of all ways to topologically sort the empty graph will be [[]].
Now, let's consider the problem of topologically sorting a non-empty graph. Consider a sequence s which is a topological sort of a non-empty graph G. We can consider the first element of s, which we will call x, and the sequence of all the rest of the elements, which we call s'.
We know that x must be a node in G, and we know that x cannot have any predecessors in G. In other words, there can be no node y such that (y, x) is an edge.
We also know that s' must be a topological sort of G'_x, which is G but with the node x (and all edges connecting to it) removed.
So to get all the topological sortings of G, we must find all sequences with initial element x and remaining elements s', such that x is an element of G with no predecessors and s' is a topological sorting of G'_x.
const remove_node = (g, x) =>
g.flatMap((node) => node["id"] == x ?
[] :
{"id": node["id"],
"prev": node["prev"].filter((id) => id != x)});
const topological_sorts = (g) =>
g.length == 0 ?
[[]] :
g.filter((node) => node["prev"].length == 0)
.map((node) => node["id"])
.flatMap((id) =>
topological_sorts(remove_node(g, id)).map((sorting) =>
[id].concat(sorting)));
const remove_nan = (g) => g.map((node) =>
({ "id" : node.id,
"prev": node.prev.filter((predecessor) =>
predecessor !== "NaN") } ));
const get_answer = (g) => topological_sorts(remove_nan(g));
Calling get_answer on your graph will result in the correct answer.

Related

Rendering two arrays simultaneously in React, starting at first element

TLDR: I'm mapping over an array of arrays and mapping over each of those arrays to render data. React is rendering the each element of each array in order, so that the entire first array is rendered before the second array begins rendering. I would like to render the first element of each array, then the second element of each array, etc.
I'm trying to build a masonry component! From what I can tell, these are traditionally made by calculating the length of each column, and appending new items to the shortest column, then re-calculating and doing it again.
This is an obviously expensive process, and a hacky solution I came up with is:
Sort data (images with known heights) from lowest to highest
Split these evenly into a number of arrays equal to the desired number of columns (end result may have columns of different lengths, but it will approximate close enough for my needs)
Randomize the arrays to give variation in heights
Display data from arrays in flexbox columns
Here's the relevant code for that:
const { columns, height, width } = useStore()
const [ content, setContent ] = useState<any[]>([])
useEffect(() => {
// Sort data by image height
data.sort((a: item, b: item) =>
a.images.downsized.height - b.images.downsized.height
)
// Create array of arrays
let columnsArray: any[] = []
for (let i = 0; i < columns; i++) {
let newArray: item[] = []
columnsArray.push(newArray)
}
// Populate each array with images
let index = 0
data.forEach((item: item) => {
columnsArray[index].push(item)
if (index == columnsArray.length - 1) { index = 0 }
else { index++ }
})
// Randomize each array to give masonry appearance
const shuffle = (array: any[]) => {
// Fisher-Yates algorithm
let m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--)
t = array[m]
array[m] = array[i]
array[i] = t
}
return array
}
columnsArray.forEach(array => shuffle(array))
// Move new data structure to state
setContent(columnsArray)
}, [data])
This works really well as it performs pretty light computations and leverages CSS and flexbox to make up the difference.
The problem is that it renders each column in order. If I'm rendering two columns of 100 items each, it doesn't make sense to load the 100th item of the first column before the 1st item of the second.
Here's the code for rendering:
{content.length > 0 && content.map((contentArray: item[]) =>
<Col>
{contentArray.map((item: item) =>
<MasonryItem
src={item.images.downsized.url}
alt={item.title}
/>
)}
</Col>
)}
Instead, I'd like React to render the first item of the first array, then the first item of the second, etc.
Is there a way to do this? Thanks in advance!

taking one element of each type from a list and only taking the first occurrence not working as expected

So this function is supposed to pars an array of object and only take the first occurrence of each type of object there is like 4 different ones and each ones has a dozen of entries.
LastReadingOfEach(measurements){
let devicesList = []
devicesList.push(measurements.map((device) => {if (!devicesList.includes(device.Type) ) { return device} else console.log("removed :")}))
console.log("devicesList : ", devicesList)
}
I created an array and thought I could just push in a device of each type, but I think it doesn't compare against the array as it fill up because it at the end I find the devicesList has all elements on measurements
I believe what you're looking for can be done with a reducer and findIndex. If find index fails to match the device type it will return -1, we know that we don't have that type yet so it gets pushed into the aggregation.
LastReadingOfEach(measurements){
const devicesList = measurements.reduce((agg, device) => {
if (agg.findIndex(x => x.type === device.type) === -1) {
agg.push(device)
}
return agg
}, [])
console.log("devicesList : ", devicesList)
}

Multiple Filter Methods

I'm trying to solve this problem.
I'm creating a filter method, that has to return me a filtered array based on my preference.
The array is containing some sports bet info, like the odd quote, the starting date, the name of the bookmaker, etc, and I'm trying (for the moment) to just filter based on the min and max odds
First of all, into my "Parent" react component, I receive some info from his child, and those info are stored in an object.
The object is like that:
filters = {minOdd: "", maxOdds:"", ecc...}
And the filter method is like:
setFilters = () => {
const odds = this.state.odds
const filters = this.state.filters
const newOdds = odds.filter((odd) => odd.quota > filters.quotaMin)
.filter((odd) => odd.quota < filters.quotaMax)
}
Where "quota" means "odd", quotaMin means "min odd" and "quotaMax" means max odd
Now, if I set the min and the max odd into my child component, the function returns me an array containing all the right odds. But if I set just one of the 2 filters, this function returns me back an empty object.
I'n my opinion, the problem is that if i don't set one of the 2 value, the filter method compares the odd this a value that is like modd.quota < filters.quotaMax, where filters.quotaMax could be = to "".
Soo i have to not allow the filter method to filter value that are = to "".
If someone can give my an advice!
Thanks in advice!
Use fallback values for the undefined filters.
If either quotaMax or quotaMin is not defined, you are (most likely, haven't seen the structure of a bet) comparing a Number against undefined, which always results in false:
1 < undefined; // -> false
1 > undefined; // -> false
As fallback values, you can use negative and positive infinity. To be honest, it doesn't matter which values you use as long as the fallback for quotaMin is guaranteed to be smaller than the lowest quota and the fallback for quotaMax is guaranteed to be higher than the highest quota.
const newOdds = odds
.filter(odd => odd.quota > (filters.quotaMin || -Infinity))
.filter(odd => odd.quota < (filters.quotaMax || Infinity));
Side note:
You can make your code run faster by merging both predicates into one with AND && (saves one iteraton/filtering).
const newOdds = odds
.filter(odd => odd.quota > (filters.quotaMin || -Infinity) &&
odd.quota < (filters.quotaMax || Infinity));
I guess you just need to handle that case then where quotaMax is undefined/"":
const newOdds = odds.filter((odd) => odd.quota > filters.quotaMin)
.filter((odd) => {
if (filters.quotaMax) {
return odd.quota < filters.quotaMax
} else {
// you decide what should happen in this case..
// return true/false
})

Sort arrays, variables or objects in Javascript

Im new to javascript but I have already made some scripts that really make a difference in my workflow. However I am now embarking on a project that forces me to sort data in a way I dont know howto do in Javascript. I will try to explain what I need to do as if my data was in excel but it isnt, I have only been able to put the data in 4 different arrays:
pagenumber[1,2,3,4,5] //only numbers
zipcode[77889,99887,33667,11122,44559] // only numbers
streetname[Hillroad, Hillroad, Baghdad Street, Hongway, Chinatown] //only letters
roadnumber[55,27,1,13,16] //only numbers
I would like to sort them like this, first by the zipcode, then by the roadname, then by the even roadnumbers descending, then by the odd roadnumbers ascending.
According to this new sorting I want to generate a new pagenumber but I want it to somehow relate to the (old) variable "pagenumber" so I can locate the old page and extract it to a new document with new pagenumbers. I am not asking you guys to write all the code for me but I need a little bit of advice to know firstly if it is possible to do which I think it is, secondly if it is right of me to put the data in four different arrays, thirdly if ther is any (ofcourse) smarter way to save the data so they relate to eachother more closely. Give me your thoughts. Also tips of where and what I should read is appreciated. Thank you all for the answers. However I want to point out that I write my code in Acrobat DC not for the web.
I suppose the items in your arrays are tied. So you should use [{},{},{},{},{}] instead of 4 arrays.
var items = [{pagenumber:1,zipcode:77889,streetname:Hillroad,roadnumber:55},{...},{...},{...},{...}]
Then sort each key-value property one-by-one, like below:
var x= [ {a:2,b:2,c:3}, {a:1,b:1,c:1}, {a:1,b:2,c:3}, {a:2,b:2,c:2} ];
x.sort(function(item1, item2){
var sort_a = item1.a-item2.a;
if (sort_a) return sort_a;
var sort_b = item1.b-item2.b;
if (sort_b) return sort_b;
var sort_c = item1.c-item2.c;
if (sort_c) return sort_c;
})
Or simplify it to be
x.sort(function(item1, item2){
return (item1.a-item2.a) || (item1.b-item2.b) || (item1.c-item2.c);
})
Given the data:
var pagenumber=[1,2,3,4,5]; //only numbers
var zipcode=[77889,99887,33667,11122,44559]; // only numbers
var streetname=['Hillroad', 'Hillroad', 'Baghdad Street', 'Hongway', 'Chinatown']; //only letters
var roadnumber=[55,27,1,13,16]; //only numbers
First, you need to make your data more easily manageable
var data = pagenumber.map(function(itemValue, index) {
return {
pagenumber:itemValue, // == pagenumber[index]
zipcode:zipcode[index],
streetname:streetname[index],
roadnumber:roadnumber[index]
};
});
Then sort it
data.sort(function(a, b) {
if (a.zipzode != b.zipcode) {
// numeric
return a.zipcode - b.zipcode;
}
if (a.streetname != b.streetname) {
// alpha
return a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0;
}
if (a.roadnumber % 2 != b.roadnumber % 2) {
// even before odd
return b.roadnumber % 2 - a.roadnumber % 2;
}
// numeric
return a.roadnumber - b.roadnumber;
});
borrowing from another answer, that can be simplified to
data.sort(function(a, b) {
return (a.zipcode - b.zipcode) || (a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0) || (b.roadnumber % 2 - a.roadnumber % 2) || (a.roadnumber - b.roadnumber);
});
Personally, I don't use the intermediate step when I can avoid it ... so the following is equivalent to bot the map and sort in one chained command
var sortedData = pagenumber.map(function(itemValue, index) {
return {
pagenumber:itemValue,
zipcode:zipcode[index],
streetname:streetname[index],
roadnumber:roadnumber[index]
};
}).sort(function(a, b) {
return (a.zipcode - b.zipcode) || (a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0) || (b.roadnumber % 2 - a.roadnumber % 2) || (a.roadnumber - b.roadnumber);
});
// sorting zipcode in ascending order
zipcode.sort();
// sorting streetname in ascending order
streetname.sort();
// fetching evenroad numbers
var roadnumbereven=roadnumber.filter(function(element, index, array) {
return (element % 2 === 0);
});
// fetching odd roadnumbers
var roadnumberodd = roadnumber.filter(function(element, index, array) {
return (element % 2 !== 0);
});
// sorting even road numbers in ascending order
roadnumbereven.sort();
// sorting odd road numbers in descending order
roadnumberodd.sort(function(a,b){ return b-a; });
// merging roadnumbers(even/odd)
roadnumber = roadnumbereven.concat(roadnumberodd);
console.log(roadnumber);

Creating series from series in d3

I have data of this form (simplified, but assume 20 columns between Admin and Mining):
Date,Series,Admin,Mining,CPI
1990,Ordinary Time Earnings,20,30,96
1991,Ordinary Time Earnings,22,33,100
1990,Total Earnings,25,38,96
1991,Total Earnings,29,43,100
Which I separate out into two series like this:
d3.csv("avgearnings_v1_1.csv", function(error, data) {
if (error) throw error;
OrdinaryTimeEarnings = data
.filter(function(d) {
if(d.Series == 'Ordinary Time Earnings')
return d;
});
TotalEarnings = data
.filter(function(d) {
if(d.Series == "Total Earnings")
return d;
});
And can get that to display on a graph without any issue. What I want to do next is create two more series:
OrdinaryTimeEarningsReal = OrdinaryTimeEarnings;
TotalEarningsReal = TotalEarnings;
And then recalculate those new series. Basically:
For any column that is not Date/Series/CPI
Take the CPI value for that year
Individually divide each of the Mining-Admin columns by the CPI and multiply by 100.
So: New Value = ([Old Value]/[CPI])*100
My code is terrible but I can get the correct values using this:
OrdinaryTimeEarningsReal
.forEach(function (z,i) {
var CPI = z["CPI"];
d3.map(z, function(b) {return b;})
.forEach(function (c) {
if(c !== "Date" && c !== "Series" && c !== "CPI" )
OrdinaryTimeEarningsReal[i][c] = ((z[c])/(CPI))*100;
});
});
But, when I do this it is somehow also updating the original OrdinaryTimeEarnings series, such that they equal each other and the original data in OrdinaryTimeEarnings is lost.
I'm not sure whether it's the fact I'm using the bare object (while iterating within it, eek!) or that the code above is actually changing the values in the original data object (and all 4 of the series I've created after are just references to it).
Either way, I can't work it out! I've tried a fair few different syntax forms but can't work it out. Help would be greatly appreciated to achieve this.
If you indeed use this code to "duplicate" your arrays:
OrdinaryTimeEarningsReal = OrdinaryTimeEarnings;
TotalEarningsReal = TotalEarnings;
then you mentioned it right, when you said that they reference the same object. In JavaScript, arrays are mutable, and using the code above you just created 2 new variables with a reference to the existing array in the memory.
In order to deep clone your array of objects, use this method:
OrdinaryTimeEarningsReal = JSON.parse(JSON.stringify(OrdinaryTimeEarnings));
TotalEarningsReal = JSON.parse(JSON.stringify(TotalEarnings));
This will create duplicates of the array and assign them to the new variables, so that when you'll edit them, the initial arrays will remain unaffected.
Now, regarding your code, it's a bit too complex. If I understood correctly what are you trying to achieve, you could simplify it as follows:
OrdinaryTimeEarningsReal
.forEach(function (z,i) {
for (var c in z) {
if (z.hasOwnProperty(c) && c !== "Date" && c !== "Series" && c !== "CPI" )
z[c] = z[c] / z.CPI * 100;
});
});
Good luck!
If I understand correctly :
data.forEach(function(d) {
for (var key in d) {
if (key !== 'Date' && key !== 'Series' && key !== 'CPI') {
d['new' + key] = (d[key] / d.CPI) * 100;
}
}
})
console.log(data)
I have added new onto the new attributes so the new admin value is newAdmin
Implemented fiddle : https://jsfiddle.net/thatOneGuy/9ywLytjf/

Categories