From b8232c09b91df3412f703dd26c21c685bacd0321 Mon Sep 17 00:00:00 2001 From: Thomas Bock Date: Sun, 2 Apr 2023 22:07:36 +0200 Subject: [PATCH] Fix order of edge or vertex attributes When edge or vertex attributes are added via `add.attributes.to.network`, as of igraph version 1.4.0, existing attributes are not re-added or re-ordered. That is, if attributes `name` and `type` already exist and ones adds the new attributes `kind` and `type`, then `kind` is added after `type` as `type` already existed. However, this breaks the ordering of the attributes and leads to failing tests, as the order of the attributes is also tested in some of our tests. To fix this problem, remove and re-add an attribute that was already present before. This way, we make sure that the new attributes are added in the expected order. (The only exception is the `name` attribute: This attribute is expected to be the first attribute, and it should not be removed and readded as this could lead to serious problems.) This addresses se-sic#236. Signed-off-by: Thomas Bock --- util-networks.R | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/util-networks.R b/util-networks.R index 3237e2da..53278123 100644 --- a/util-networks.R +++ b/util-networks.R @@ -1367,11 +1367,15 @@ add.attributes.to.network = function(network, type = c("vertex", "edge"), attrib ## get type type = match.arg(type, several.ok = FALSE) - ## get corresponding attribute function + ## get corresponding attribute functions if (type == "vertex") { - attribute.function = igraph::set.vertex.attribute # sprintf("igraph::set.%s.attribute", type) + attribute.set.function = igraph::set.vertex.attribute # sprintf("igraph::set.%s.attribute", type) + attribute.get.function = igraph::get.vertex.attribute # sprintf("igraph::get.%s.attribute", type) + attribute.remove.function = igraph::remove.vertex.attribute # sprintf("igraph::remove.%s.attribute", type) } else { - attribute.function = igraph::set.edge.attribute # sprintf("igraph::set.%s.attribute", type) + attribute.set.function = igraph::set.edge.attribute # sprintf("igraph::set.%s.attribute", type) + attribute.get.function = igraph::get.edge.attribute # sprintf("igraph::get.%s.attribute", type) + attribute.remove.function = igraph::remove.edge.attribute # sprintf("igraph::remove.%s.attribute", type) } ## iterate over all wanted attribute names and add the attribute with the wanted class @@ -1385,6 +1389,16 @@ add.attributes.to.network = function(network, type = c("vertex", "edge"), attrib if (lubridate::is.POSIXct(default.value)) { attr(default.value, "tzone") = TIMEZONE } + + ## check if the attribute is already present. If so, remove it and re-add it (to keep the intended order). + ## only exception from this: the name attribute is not removed and re-added, as this would lead to problems. + if (!is.null(attribute.get.function(network, attr.name)) && attr.name != "name") { + logging::logwarn("Attribute %s has already been present, but is re-added now.", attr.name) + present.value = attribute.get.function(network, attr.name) + network = attribute.remove.function(network, attr.name) + default.value = present.value + } + ## add the new attribute to the network with the proper class network = attribute.set.function(network, attr.name, value = default.value) }