diff --git a/ui/package.json b/ui/package.json
index 56ede1bd9..d6715d304 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -34,7 +34,6 @@
"mobx-react-lite": "2.0.7",
"mobx-stored": "1.1.0",
"moment": "2.26.0",
- "object-hash": "2.0.3",
"promise-retry": "1.1.1",
"prop-types": "15.7.2",
"qs": "6.9.4",
diff --git a/ui/src/Common/Hash.js b/ui/src/Common/Hash.js
new file mode 100644
index 000000000..a2dbd67d3
--- /dev/null
+++ b/ui/src/Common/Hash.js
@@ -0,0 +1,25 @@
+// https://stackoverflow.com/a/58208791/1154047
+const normalize = (sortingFunction) => {
+ return function (key, value) {
+ if (typeof value === "object" && !Array.isArray(value)) {
+ return Object.entries(value)
+ .sort(sortingFunction || undefined)
+ .reduce((acc, entry) => {
+ acc[entry[0]] = entry[1];
+ return acc;
+ }, {});
+ }
+ return value;
+ };
+};
+
+// https://stackoverflow.com/a/15710692/1154047
+const hashString = (s) =>
+ s.split("").reduce((a, b) => {
+ a = (a << 5) - a + b.charCodeAt(0);
+ return a & a;
+ }, 0);
+
+const hashObject = (o) => hashString(JSON.stringify(o, normalize(), 2));
+
+export { normalize, hashString, hashObject };
diff --git a/ui/src/Components/LabelSetList/index.js b/ui/src/Components/LabelSetList/index.js
index 69fc17437..4dca3c903 100644
--- a/ui/src/Components/LabelSetList/index.js
+++ b/ui/src/Components/LabelSetList/index.js
@@ -3,10 +3,9 @@ import PropTypes from "prop-types";
import { useLocalStore, useObserver } from "mobx-react";
-import hash from "object-hash";
-
import { AlertStore } from "Stores/AlertStore";
import { IsMobile } from "Common/Device";
+import { hashObject } from "Common/Hash";
import { StaticLabel } from "Components/Labels/StaticLabel";
import { PageSelect } from "Components/Pagination";
@@ -22,7 +21,7 @@ const GroupListToUniqueLabelsList = (groups) => {
group.shared.labels,
alert.labels
);
- const alertHash = hash(alertLabels);
+ const alertHash = hashObject(alertLabels);
alerts[alertHash] = alertLabels;
}
}
@@ -50,9 +49,9 @@ const LabelSetList = ({ alertStore, labelsList }) => {
(pagination.activePage - 1) * maxPerPage,
pagination.activePage * maxPerPage
)
- .map((labels) => (
+ .map((labels, index) => (
{Object.entries(labels).map(([name, value]) => (
diff --git a/ui/src/Components/ManagedSilence/SilenceDetails.js b/ui/src/Components/ManagedSilence/SilenceDetails.js
index a46ea2b61..a8f8d1da4 100644
--- a/ui/src/Components/ManagedSilence/SilenceDetails.js
+++ b/ui/src/Components/ManagedSilence/SilenceDetails.js
@@ -1,8 +1,6 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
-import hash from "object-hash";
-
import moment from "moment";
import Moment from "react-moment";
@@ -139,9 +137,9 @@ const SilenceDetails = ({
className="flex-shrink-1 flex-grow-1 mw-1p"
style={{ minWidth: "0px" }}
>
- {silence.matchers.map((matcher) => (
+ {silence.matchers.map((matcher, index) => (
{matcher.name}
diff --git a/ui/src/Components/NavBar/FilterInput/History.js b/ui/src/Components/NavBar/FilterInput/History.js
index 2102c3998..c012705ac 100644
--- a/ui/src/Components/NavBar/FilterInput/History.js
+++ b/ui/src/Components/NavBar/FilterInput/History.js
@@ -4,8 +4,6 @@ import PropTypes from "prop-types";
import { useObserver, useLocalStore } from "mobx-react";
import { localStored } from "mobx-stored";
-import hash from "object-hash";
-
import { Manager, Reference, Popper } from "react-popper";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -79,10 +77,10 @@ const HistoryMenu = ({
{filters.length === 0 ? (
Empty
) : (
- filters.slice(0, maxItems).map((historyFilters) => (
+ filters.slice(0, maxItems).map((historyFilters, index) => (