Related
I am new to react and I'm having some challenges, I want to have real time updating ui using data (complex json object) fetched from a mysql database using axios in react. I was using useEffect to handle the UI rerender however I am having challenges because its causing infinite rerendering, the json data is too complex for the useEffect dependency array. Which approach should I use, will sockets help?
useEffect(() => {
let isMounted = true;
Axios.post('http://localhost:3001/update_irrigation', {
sql: sql
}).then(res => {
if (isMounted) setIrrigator(res.data.results);
})
.catch(err => {
console.log(err);
})
return () => { isMounted = false };
})
here is a sample of the json object array, which is dynamic :
[
{
"ID": 9,
"Sensor_1": 9,
"Sensor_2": 65,
"Sensor_3": 43,
"Sensor_4": 35,
"Sensor_5": 55,
"Sensor_6": 56,
"Sensor_7": 12,
"Sensor_8": 32,
"Sensor_9": 90,
"Sensor_10": 99,
"Solenoid_1": "open",
"Solenoid_2": "open",
"Solenoid_3": "closed",
"Solenoid_4": "closed",
"Solenoid_5": "open",
"Time": "2022-02-17T14:27:49.000Z"
},
{
"ID": 5,
"Sensor_1": 40,
"Sensor_2": 3,
"Sensor_3": 4,
"Sensor_4": 12,
"Sensor_5": 43,
"Sensor_6": 56,
"Sensor_7": 12,
"Sensor_8": 32,
"Sensor_9": 90,
"Sensor_10": 99,
"Solenoid_1": "open",
"Solenoid_2": "open",
"Solenoid_3": "closed",
"Solenoid_4": "closed",
"Solenoid_5": "open",
"Time": "2022-02-17T05:00:00.000Z"
}
]
O you should add an empty array ([]) immediately after the return function that should stop multiple re-rendering
useEffect=()=>{
Do something,
return function {}
, []}
Hi I have this json file with these partial data, and I will be user 1 in this example, and need to create a graph vertex and connect it to my user for all my friends and then create a vertex and connect my friend friends and so on.
[
{
"id": 1,
"name": "Me",
"friends": [25, 24, 16, 8, 13, 12, 7, 15]
},
{
"id": 2,
"name": "Anne Emerson",
"friends": [3, 21, 4, 20, 24, 5, 7, 12, 18]
},
{
"id": 3,
"name": "Ashley Hopper",
"friends": [2, 22, 10, 20, 25, 11]
},
{
"id": 4,
"name": "Alba Gates",
"friends": [2, 17, 21, 11, 7, 13]
},
{
"id": 5,
"name": "Louise Pennington",
"friends": [2, 23, 22, 15, 20, 24]
},
{
"id": 6,
"name": "Shields Gilliam",
"friends": [26]
},
{
"id": 7,
"name": "Freida Evans",
"friends": [1, 4, 2, 9, 18, 17]
},
{
"id": 8,
"name": "Noel Serrano",
"friends": [1, 24, 18, 9, 16, 11, 23]
}
]
I tried to write a recursion function that goes throw my field list which are users with these ID's [3, 21, 4, 20, 24, 5, 7, 12, 18] like in the example. and create a graph vertex for the User I'm at and connect it with its parent user. and keeps going throw all my friends first then goes throw my friend friends and so on until I discovered all of the friends related to my user weather its directly or indirectly.
Graph methods to use:
create a parent vertex (ME):
let me = graph.createVertex(data: "Me")
connect vertexes together:
graph.add(.undirected, from: me, to: user25, weight: 1)
User model:
struct User: Codable, Identifiable, Hashable {
enum CodingKeys: CodingKey {
case id
case name
case friends
}
var id: Int
var name: String
var friends: [Int]
}
what I tried so far using nested loops:
for (index, i) in friends.enumerated() {
print("i: \(i)")
let getUserByID = datas.users.filter{ $0.id == index}
for (index, i) in getUserByID.enumerated() {
print(i.friends)
}
}
where friends is let friends = [25, 24, 16, 8, 13, 12, 7, 15]
The logic isn't hitting me yet and I wasn't able to achieve this in Swift 5 can anyone help ?
Sample OutPut for my friends mutual list:
John Doe - edge distance: 2, Mutual friends: 5
Bob Kemp - edge Distance: 2, Mutual friends: 3
Bryce Holmes - edge Distance: 3
James Smith - edge Distance: 3
The list should be ranked firstly by edge distance and secondly
by number of mutual friends.
I was given a list of apps along with their ratings:
let appRatings = [
"Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2]
]
I want to write a func that takes appRating as input and return their name and average rating, like this.
["Calendar Pro": 3,
"The Messenger": 3,
"Socialise": 2]
Does anyone know how to implement such a method that it takes (name and [rating]) as input and outputs (name and avgRating ) using a closure inside the func?
This is what I have so far.
func calculate( appName: String, ratings : [Int]) -> (String, Double ) {
let avg = ratings.reduce(0,+)/ratings.count
return (appName, Double(avg))
}
Fundamentally, what you're trying to achieve is a mapping between one set of values into another. Dictionary has a function for this, Dictionary.mapValues(_:), specifically for mapping values only (keeping them under the same keys).
let appRatings = [
"Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2]
]
let avgAppRatings = appRatings.mapValues { allRatings in
return computeAverage(of: allRatings) // Dummy function we'll implement later
}
So now, it's a matter of figuring out how to average all the numbers in an Array. Luckily, this is very easy:
We need to sum all the ratings
We can easily achieve this with a reduce expression. StWe'll reduce all numbers by simply adding them into the accumulator, which will start with 0
allRatings.reduce(0, { accumulator, rating in accumulator + rate })
From here, we can notice that the closure, { accumulator, rating in accumulator + rate } has type (Int, Int) -> Int, and just adds the numbers together. Well hey, that's exactly what + does! We can just use it directly:
allRatings.reduce(0, +)
We need to divide the ratings by the number of ratings
There's a catch here. In order for the average to be of any use, it can't be truncated to a mere Int. So we need both the sum and the count to be converted to Double first.
You need to guard against empty arrays, whose count will be 0, resulting in Double.infinity.
Putting it all together, we get:
let appRatings = [
"Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2]
]
let avgAppRatings = appRatings.mapValues { allRatings in
if allRatings.isEmpty { return nil }
return Double(allRatings.reduce(0, +)) / Double(allRatings.count)
}
Add in some nice printing logic:
extension Dictionary {
var toDictionaryLiteralString: String {
return """
[
\t\(self.map { k, v in "\(k): \(v)" }.joined(separator: "\n\t"))
]
"""
}
}
... and boom:
print(avgAppRatings.toDictionaryLiteralString)
/* prints:
[
Socialise: 2.0
The Messenger: 3.0
Calendar Pro: 3.375
]
*/
Comments on your attempt
You had some questions as to why your attempt didn't work:
func calculate( appName: String, ratings : [Int]) -> (String: Int ) {
var avg = ratings.reduce(0,$0+$1)/ratings.count
return appName: sum/avg
}
$0+$1 isn't within a closure ({ }), as it needs to be.
appName: sum/avg isn't valid Swift.
The variable sum doesn't exist.
avg is a var variable, even though it's never mutated. It should be a let constant.
You're doing integer devision, which doesn't support decimals. You'll need to convert your sum and count into a floating point type, like Double, first.
A fixed version might look like:
func calculateAverage(of numbers: [Int]) -> Double {
let sum = Double(ratings.reduce(0, +))
let count = Double(numbers.count)
return sum / count
}
To make a function that processes your whole dictionary, incoroprating my solution above, you might write a function like:
func calculateAveragesRatings(of appRatings: [String: [Int]]) -> [String: Double?] {
return appRatings.mapValues { allRatings in
if allRatings.isEmpty { return nil }
return Double(allRatings.reduce(0, +)) / Double(allRatings.count)
}
}
This a simple solution that takes into account that a rating is an integer:
let appRatings = [
"Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2]
]
let appWithAverageRating: [String: Int] = appRatings.mapValues { $0.reduce(0, +) / $0.count}
print("appWithAverageRating =", appWithAverageRating)
prints appWithAverageRating = ["The Messenger": 3, "Calendar Pro": 3, "Socialise": 2]
If you'd like to check whether an app has enough ratings before returning an average rating, then the rating would be an optional Int:
let minimumNumberOfRatings = 0 // You can change this
var appWithAverageRating: [String: Int?] = appRatings.mapValues { ratingsArray in
guard ratingsArray.count > minimumNumberOfRatings else {
return nil
}
return ratingsArray.reduce(0, +) / ratingsArray.count
}
If you'd like the ratings to go by half stars (0, 0.5, 1, ..., 4.5, 5) then we could use this extension:
extension Double {
func roundToHalf() -> Double {
let n = 1/0.5
let numberToRound = self * n
return numberToRound.rounded() / n
}
}
Then the rating will be an optional Double. Let's add an AppWithoutRatings and test our code:
let appRatings = [
"Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2],
"AppWithoutRatings": []
]
let minimumNumberOfRatings = 0
var appWithAverageRating: [String: Double?] = appRatings.mapValues { ratingsArray in
guard ratingsArray.count > minimumNumberOfRatings else {
return nil
}
let rating: Double = Double(ratingsArray.reduce(0, +) / ratingsArray.count)
return rating.roundToHalf()
}
And this prints:
appWithAverageRating = ["Calendar Pro": Optional(3.0), "Socialise": Optional(2.0), "The Messenger": Optional(3.0), "AppWithoutRatings": nil]
I decided to make an Dictionary extension for this, so it is very easy to use in the future.
Here is my code I created:
extension Dictionary where Key == String, Value == [Float] {
func averageRatings() -> [String : Float] {
// Calculate average
func average(ratings: [Float]) -> Float {
return ratings.reduce(0, +) / Float(ratings.count)
}
// Go through every item in the ratings dictionary
return self.mapValues { $0.isEmpty ? 0 : average(ratings: $0) }
}
}
let appRatings: [String : [Float]] = ["Calendar Pro": [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise": [2, 1, 2, 2, 1, 2, 4, 2]]
print(appRatings.averageRatings())
which will print the result of ["Calendar Pro": 3.375, "Socialise": 2.0, "The Messenger": 3.0].
Just to make the post complete another approach using reduce(into:) to avoid using a dictionary with an optional value type:
extension Dictionary where Key == String, Value: Collection, Value.Element: BinaryInteger {
var averageRatings: [String : Value.Element] {
return reduce(into: [:]) {
if !$1.value.isEmpty {
$0[$1.key] = $1.value.reduce(0,+) / Value.Element($1.value.count)
}
}
}
}
let appRatings2 = ["Calendar Pro" : [1, 5, 5, 4, 2, 1, 5, 4],
"The Messenger": [5, 4, 2, 5, 4, 1, 1, 2],
"Socialise" : [2, 1, 2, 2, 1, 2, 4, 2] ]
let keySorted = appRatings2.averageRatings.sorted(by: {$0.key<$1.key})
keySorted.map{ print($0,$1) }
Calendar Pro 3
Socialise 2
The Messenger 3
I am new to MongoDB queries, and I understand the basics of find. However I haven't yet come to grips with $lookup, $project, $aggregate, $match, etc, which is pretty much needed to do anything fancy. And I need a fancy query 😃 See tricky issue below:
Collections:
schema
{ _id: 1, name: “Schema1”, }
{ _id: 2, name: “Schema2”, }
device:
{ _id: 1, schema: 1, organisation: 2 }
{ _id: 2, schema: 1, organisation: 2 }
{ _id: 3, schema: 2, organisation: 2 }
field:
{ _id: 1, organisation: 2, name: “cost”, displayType: “number” }
{ _id: 2, organisation: 2, name: “retail”, displayType: “number” }
{ _id: 3, organisation: 2, name: “project”, displayType: “string” }
fieldvalue
{ _id: 1, device: 1, field: 1, organisation: 2, value: 2000 }
{ _id: 2, device: 1, field: 2, organisation: 2, value: 3000 }
{ _id: 3, device: 2, field: 1, organisation: 2, value: 1000 }
{ _id: 4, device: 2, field: 2, organisation: 2, value: 1000 }
{ _id: 5, device: 3, field: 1, organisation: 2, value: 500 }
{ _id: 6, device: 1, field: 3, organisation: 2, value: “Project1” }
{ _id: 7, device: 2, field: 3, organisation: 2, value: “Project2” }
{ _id: 8, device: 3, field: 3, organisation: 2, value: “Project2” }
I want to query FieldValue.
I want to $sum all “value” where field is 1
BUT only those which “shares” device with field 3 with value “Project2”.
As parameters I have:
The id of the field I want to sum, e.g. 1 (cost)
The id of the "project” field, e.g. 3 (project)
The value of the “project” field (id 3) which also must be met in order to qualify for summation.
So my query should only $sum the “value” of id’s: 3 and 5
How can I do that in Mongo Query?
And would it be possible to add more contraints? E.g. the “schema” of the “device” must be 3, which should then result in a $sum of just id: 5
Lets say I have the following data in MongoDB:
{
timePeriod: 1,
foo: 1,
bar: 6,
baz: 8
},
{
timePeriod: 2,
foo: 7,
bar: 5,
baz: 2
},
{
timePeriod: 3,
foo: 3,
bar: 6,
baz: 9
},
{
timePeriod: 4,
foo: 4,
bar: 5,
baz: 4
},
{
timePeriod: 5,
foo: 0,
bar: 8,
baz: 1
},
{
timePeriod: 6,
foo: 6,
bar: 1,
baz: 0
},
After sorting the objects by timePeriod, I am interested in examining sequences that appear between each sorted object, and attaching this sequence to the original data.
So between timePeriod 1 and timePeriod 3, the sequence of foo is 1, 7, 3, and between time Period 4 and timePeriod 6, the sequence of foo is 4, 0, 6.
First thing - would anyone know a way to do this as a MongoDB. It seems straightforward (and easy to do with Python/Pandas, but I don't know MongoDB well enough. I am expecting the the end result of the query to look like the code below.
Second thing - if I wanted to do some operations on this sequence I get out of the query(like find out the absolute distance between each number in the sequence and the next, so 1, 7, 3 becomes 6, 4) is that possible to do in MongoDB?
{
timePeriod: 1,
foo: 1,
bar: 6,
baz: 8,
fooSeqFromThisTimeOnwards: [1, 7, 3]
},
{
timePeriod: 2,
foo: 7,
bar: 5,
baz: 2,
fooSeqFromThisTimeOnwards: [7, 3, 4]
},
{
timePeriod: 3,
foo: 3,
bar: 6,
baz: 9,
fooSeqFromThisTimeOnwards: [3, 4, 0]
},
{
timePeriod: 4,
foo: 4,
bar: 5,
baz: 4,
fooSeqFromThisTimeOnwards: [4, 0, 6]
},
{
timePeriod: 5,
foo: 0,
bar: 8,
baz: 1,
fooSeqFromThisTimeOnwards: [0, 6, nan]
},
{
timePeriod: 6,
foo: 6,
bar: 1,
baz: 0,
fooSeqFromThisTimeOnwards: [6, nan, nan]
},
UPDATE: Just a quick addendum to this - the more I look into this, it seems it is just not possible to do this (??) - only solution I can think of is using some kind of forEach thing, iterate and get the next n foo values that are $gt current timePeriod, and attach those values to the object I am at in the iteration. Then I can group by sequences and return similar sequence objects.
But that approach seems really expensive, like if I have a web service that makes a lot of requests for different sized sequences. So is it better to just have sequences hard coded into the original data and then just regex query this to pick sequences with n elements when needed? But if I do that, my original data seems to have some redundancy in it and becomes a lot bigger.
Just not really sure of the best approach here...