Introduction:
I am trying to get my head around how to update deeply nested data using Equatable
and .copyWith
methods and in doing so, I have made a complex .map
function.
Problem:
Ideally, I would like to use some iterative functional programming technique to simplify the getNewHierarchy()
function; but the solution eludes me. The minimal code works as expected, and I can update the deeply nested data in my code. But, it has to lead to a complex .map
function (labelled in the minimum viable code '//TODO: I would like to clean this method and make it less complex.'). Try as I might, I cannot find a way to reduce the complexity of this function using a composite design pattern or another functional programming technique without breaking it.
Objective:
This is a remake of a project that I am working on that has a bigger purpose than the question. It is the most basic of minimal viable code for the questions. In the main method, I patch together a deeply nested data object, and then I can 'getNewHierarchy()' to rename the nested object of choice. In this case, I have randomly inserted an object of the nested class for example purposes only. The objective is to make the getNewHierarchy()
function less complex without breaking my code. This method maps through the NestedClass object looking for the object of a class and changes it's name. If it does not find the object, it goes into the list of children looking for the class and changes its name when it finds the correct object. This code is very messy.
Question:
How can I simplify the complex .map
function that works with deeply nested data?
My code:
// add equatable: ^2.0.5 to pubspec.yaml
import 'package:equatable/equatable.dart';
void main() {
/// Minimum viable code to reproduce the issue
var greatGrandParents =
const NestedClass(name: 'Great Grand Parents', children: [
NestedClass(name: ' Grand Parents 1', children: [
NestedClass(name: ' Parents 1', children: [
NestedClass(name: ' Children 1', children: []),
NestedClass(name: ' Children 2', children: []),
]),
NestedClass(name: ' Parents 2', children: [
NestedClass(name: ' Children 3', children: []),
NestedClass(name: ' Children 4', children: []),
]),
NestedClass(name: ' Parents 3', children: [
NestedClass(name: ' Children 5', children: []),
NestedClass(name: ' Children 6', children: []),
])
]),
NestedClass(name: ' Grand Parents 2', children: [])
]);
print('=====================');
print('Original: ');
greatGrandParents.printChildrenNames();
print('=====================');
print('Updated: ');
final childToBeUpdated = greatGrandParents.children[0].children[1];
final updatedHierarchy =
getNewHierarchy('***George***', childToBeUpdated, greatGrandParents);
updatedHierarchy.printChildrenNames();
print('=====================');
}
// Model
class NestedClass extends Equatable {
final String name;
final List<NestedClass> children;
const NestedClass({
required this.name,
required this.children,
});
printChildrenNames() {
print(name);
for (var child in children) {
child.printChildrenNames();
}
}
NestedClass copyWith({
String? name,
List<NestedClass>? children,
}) {
return NestedClass(
name: name ?? this.name,
children: children ?? this.children,
);
}
@override
List<Object> get props => [name, children];
}
//TODO: I would like to clean this method and make it less complex.
NestedClass getNewHierarchy(
String newName, NestedClass person, NestedClass old) {
NestedClass copiedPerson = person.copyWith(
children: old.children
.map((e) => e.copyWith(
name: e == person ? newName : e.name,
children: e.children
.map((e) => e.copyWith(
name: e == person ? newName : e.name,
children: e.children
.map(
(e) =>
e == person ? e.copyWith(name: newName) : e,
)
.toList(),
))
.toList(),
))
.toList(),
);
return copiedPerson;
}
Appreciate if someone can advise. Thank you in advance!
Yikes, this would be much easier to solve if you, at all, ever, explained what the heck the getNewHierarchy function is supposed to do.
From what I can tell, you pass it a new name, an old version and a new version, then proceed to replace the new version's children with the old version's, but before doing that, you check each of the children against the new class and if they match, you change their name, and if they don't you keep it?
How about something like this?
NestedClass getNewHierarchy(String newName, NestedClass newValue, NestedClass oldValue) {
return newValue.copyWith(
name: oldValue == newValue ? newName : oldValue.name,
children: oldValue.children.map((v) => getNewHierarchy(newName, v, newValue)).toList(),
);
}
Also from what I can tell, your data is never equal? Is that just cuz this is an example? You already know you want to edit the greatGrandParents.children[0].children[1];
child, so why don't you update it directly???
Also if you are gonna change both the name and children, you know, the two only things in your class, there is no use for a copyWith
call, you can just use a normal constructor:
return NestedClass(
name: oldValue == newValue ? newName : oldValue.name,
children: oldValue.children.map((v) => getNewHierarchy(newName, v, newValue)).toList(),
);