簡體   English   中英

需要幫助以遞歸方式過濾嵌套的 json 對象

[英]Need help filtering nested json object recursively

我在 React 中有一個 JSON 對象“數據”,其中嵌套了其他對象,將其視為目錄/文件結構,其中層數是任意的。

const data = {
        "item1": {
            "item1.1": {
                "item1.1.1": {
                    "item1.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "ERROR"
                    }
                }
            },
            "item1.2": {
                "item1.2.1": {
                    "item1.2.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "WARNING"
                    }
                }
            }
        },
        "item2": {
            "item2.1": {
                "item2.1.1": {
                    "item2.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "WARNING"
                    }
                },
                "item2.1.2": {
                    "item2.1.2.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "OK"
                    },
                    "item2.1.2.2": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "WARNING"
                    }
                }
            }
        },
        "item3": {
            "item3.1": {
                "item3.1.1": {
                    "item3.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": [],
                        "status" : "OK"
                    }
                },
                "item3.1.2": {
                    "attr1": [],
                    "attr2": "",
                    "attr3": [],
                    "status" : "ERROR"
                }
            }
        }

    }

我正在嘗試編寫一個根據狀態值過濾對象的函數。

例如,getStatuses(data, "ERROR") 將返回狀態為“ERROR”的所有對象的列表。 預期返回值為:

{"item1.1.1.1": {"attr1": [], "attr2": "", "attr3": [], "status" : "ERROR"},
 "item3.1.2": {"attr1": [],"attr2": "", "attr3": [], "status" : "ERROR"}
}

我的想法是該函數需要遞歸地遍歷數據,然后將匹配的對象推送到一個列表中,但我的邏輯似乎有缺陷,因為我沒有得到正確的結果。

    const getStatuses= (obj, status) => {
        let result = [];
        if (obj.status === status) {
            result.push(obj)
        }

        Object.entries(obj).forEach(([, value]) => {
            //if object is not a leaf node
            if (value !== null && !Array.isArray(value) && typeof value === 'object') {
                getStatuses(value, status); //recursive call to dive in another layer
            }
        });
    }

您的函數不返回任何內容: push發生在丟失的局部變量上,並且推送的對象與鍵無關。

我會建議一個遞歸生成器,它返回成對的鍵和關聯的對象。 然后調用者可以使用Object.fromEntries將它們組裝成一個對象:

 function* iterMatches(data, status) { if (Object(data) !== data) return; // A primitive for (const [key, value] of Object.entries(data)) { if (value?.status === status) yield [key, value]; yield* iterMatches(value, status); } } const data = {"item1": {"item1.1": {"item1.1.1": {"item1.1.1.1": {"attr1": [],"attr2": "","attr3": [],"status" : "ERROR"}}},"item1.2": {"item1.2.1": {"item1.2.1.1": {"attr1": [],"attr2": "","attr3": [],"status" : "WARNING"}}}},"item2": {"item2.1": {"item2.1.1": {"item2.1.1.1": {"attr1": [],"attr2": "","attr3": [],"status" : "WARNING"}},"item2.1.2": {"item2.1.2.1": {"attr1": [],"attr2": "","attr3": [],"status" : "OK"},"item2.1.2.2": {"attr1": [],"attr2": "","attr3": [],"status" : "WARNING"}}}},"item3": {"item3.1": {"item3.1.1": {"item3.1.1.1": {"attr1": [],"attr2": "","attr3": [],"status" : "OK"}},"item3.1.2": {"attr1": [],"attr2": "","attr3": [],"status" : "ERROR"}}}}; console.log(Object.fromEntries(iterMatches(data, "ERROR")));

您不僅需要保存值(具有匹配status的對象),還需要保存鍵,因此推送到數組將不起作用。 您還需要一個持久的輸出結構(不要在每次函數運行時重新創建它)。 這可以通過在每個遞歸調用中傳遞的默認參數來完成。 在每次調用中,循環遍歷每個子屬性的鍵和值,如果值滿足條件,則將鍵和值分配給輸出。

 const data={item1:{"item1.1":{"item1.1.1":{"item1.1.1.1":{attr1:[],attr2:"",attr3:[],status:"ERROR"}}},"item1.2":{"item1.2.1":{"item1.2.1.1":{attr1:[],attr2:"",attr3:[],status:"WARNING"}}}},item2:{"item2.1":{"item2.1.1":{"item2.1.1.1":{attr1:[],attr2:"",attr3:[],status:"WARNING"}},"item2.1.2":{"item2.1.2.1":{attr1:[],attr2:"",attr3:[],status:"OK"},"item2.1.2.2":{attr1:[],attr2:"",attr3:[],status:"WARNING"}}}},item3:{"item3.1":{"item3.1.1":{"item3.1.1.1":{attr1:[],attr2:"",attr3:[],status:"OK"}},"item3.1.2":{attr1:[],attr2:"",attr3:[],status:"ERROR"}}}}; const getStatuses = (inputObj, status, outputObj = {}) => { for (const [key, value] of Object.entries(inputObj)) { if (value.status === status) { outputObj[key] = value; } else if (typeof value === 'object' && !Array.isArray(value)) { getStatuses(value, status, outputObj); } } return outputObj; } console.log(getStatuses(data, 'ERROR'));

通過調整可愛的iterate函數,我們可以輕松地迭代一棵樹。 在途中,我們收集所有具有搜索狀態的人。

此解決方案與其他解決方案相同。 只會更容易找到。

 const data={item1:{"item1.1":{"item1.1.1":{"item1.1.1.1":{attr1:[],attr2:"",attr3:[],status:"ERROR"}}},"item1.2":{"item1.2.1":{"item1.2.1.1":{attr1:[],attr2:"",attr3:[],status:"WARNING"}}}},item2:{"item2.1":{"item2.1.1":{"item2.1.1.1":{attr1:[],attr2:"",attr3:[],status:"WARNING"}},"item2.1.2":{"item2.1.2.1":{attr1:[],attr2:"",attr3:[],status:"OK"},"item2.1.2.2":{attr1:[],attr2:"",attr3:[],status:"WARNING"}}}},item3:{"item3.1":{"item3.1.1":{"item3.1.1.1":{attr1:[],attr2:"",attr3:[],status:"OK"}},"item3.1.2":{attr1:[],attr2:"",attr3:[],status:"ERROR"}}}}; function getStatuses(data, status) { var result = {} const iterate = (obj) => { if (!obj) { return; } Object.keys(obj).forEach(key => { var value = obj[key] if (typeof value === "object" && value !== null) { iterate(value) if (value.status == status) { result[key] = value; } } }) } iterate(data) return result; } console.log (getStatuses(data,"ERROR"))

您已經得到了 2 個非常好的答案,這些答案遵循了您解決問題的方式,所以我只想添加一些有點不同的東西。

如果在您的情況下性能不是一個重要因素,您可以通過將問題分成兩部分來獲得更具可讀性和可重用性的代碼,這將是 (1) 展平對象,然后 (2) 使用你想要的狀態。

您可以看到我在下面解釋的示例:

 const data = { "item1": { "item1.1": { "item1.1.1": { "item1.1.1.1": { "attr1": [], "attr2": "", "attr3": [], "status" : "ERROR" } } }, "item1.2": { "item1.2.1": { "item1.2.1.1": { "attr1": [], "attr2": "", "attr3": [], "status" : "WARNING" } } } }, "item2": { "item2.1": { "item2.1.1": { "item2.1.1.1": { "attr1": [], "attr2": "", "attr3": [], "status" : "WARNING" } }, "item2.1.2": { "item2.1.2.1": { "attr1": [], "attr2": "", "attr3": [], "status" : "OK" }, "item2.1.2.2": { "attr1": [], "attr2": "", "attr3": [], "status" : "WARNING" } } } }, "item3": { "item3.1": { "item3.1.1": { "item3.1.1.1": { "attr1": [], "attr2": "", "attr3": [], "status" : "OK" } }, "item3.1.2": { "attr1": [], "attr2": "", "attr3": [], "status" : "ERROR" } } } }; const makeFlat = (obj, flatObj = {}) => { Object.entries(obj).forEach(([key, value]) => { if (!value || typeof value !== 'object') return; if (Object.keys(value).includes("status")) { flatObj[key] = value; } else { makeFlat(value, flatObj); } }); return flatObj; } const filterByStatus = (obj, status) => { const result = {}; Object.entries(obj).forEach(([key, value]) => { if(value.status === status) { result[key] = value; } }); return result; } const flatObject = makeFlat(data); console.log(flatObject); const filteredFlatObject = filterByStatus(flatObject, "ERROR"); console.log(filteredFlatObject);

備有各種有用的功能。 其中之一是pathEntries ,它的工作原理類似於Object .entries ,但不是只返回根值,而是保留所有節點的整個路徑。 例如,其中之一可能是

  [["item2", "item2.1", "item2.1.1", "item2.1.1.1", "status"], "WARNING"]

還有一個:

  [["item2", "item2.1", "item2.1.1"], {
     "item2.1.1.1": {attr1: [], attr2: "", attr3: [], status: "WARNING"}
  }]

使用它,我們可以輕松獲取所有這些條目,過濾以僅包含具有正確status節點的那些,然后修剪路徑以僅包含最后一個節點,然后使用這些值重新水化對象

 const deepObjFilter = (pred) => (o) => Object .fromEntries ( pathEntries (o) .filter (([p, v]) => pred (v)) .map (([p, v]) => [p .at (-1), v]) ) const pathEntries = (o, p = []) => [ ... (p .length > 0 ? [[p, o]] : []), ... (Object (o) === o ? Object .entries (o) .flatMap ( ([k, v]) => pathEntries (v, [...p, Array .isArray (o) ? Number (k) : k])) : [] ) ] const data = {item1: {"item1.1": {"item1.1.1": {"item1.1.1.1": {attr1: [], attr2: "", attr3: [], status: "ERROR"}}}, "item1.2": {"item1.2.1": {"item1.2.1.1": {attr1: [], attr2: "", attr3: [], status: "WARNING"}}}}, item2: {"item2.1": {"item2.1.1": {"item2.1.1.1": {attr1: [], attr2: "", attr3: [], status: "WARNING"}}, "item2.1.2": {"item2.1.2.1": {attr1: [], attr2: "", attr3: [], status: "OK"}, "item2.1.2.2": {attr1: [], attr2: "", attr3: [], status: "WARNING"}}}}, item3: {"item3.1": {"item3.1.1": {"item3.1.1.1": {attr1: [], attr2: "", attr3: [], status: "OK"}}, "item3.1.2": {attr1: [], attr2: "", attr3: [], status: "ERROR"}}}} console .log (deepObjFilter (o => o .status === 'ERROR') (data))
 .as-console-wrapper {max-height: 100% !important; top: 0}

當然,我們也可以像這樣預先配置搜索:

const filterByStatus = (status) => deepObjFilter (o => o .status === status)
// later
filterByStatus ('ERROR') (data)

甚至

const getErrors = filterByStatus ('ERROR')
// later
getErrors (data)

這里重要的一點是,擁有有用的實用功能可以使此類問題變得非常簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM