我正在尋找一種方法來洗掉具有負值的物件屬性。盡管此處提供了現有解決方案,但它僅適用于無深度物件。我正在尋找一種解決方案來消除任何深度的負面物件屬性。
這需要一個遞回解決方案,我做了一個讓我進步的嘗試,但仍然不完全是。
考慮以下stocksMarkets資料。這個結構故意弄得亂七八糟,以表明我希望無論深度如何都消除負面屬性。
const stocksMarkets = {
tokyo: {
today: {
toyota: -1.56,
sony: -0.89,
nippon: -0.94,
mitsubishi: 0.65,
},
yearToDate: {
toyota: -75.95,
softbank: -49.83,
canon: 22.9,
},
},
nyc: {
sp500: {
ea: 8.5,
tesla: -66,
},
dowJones: {
visa: 3.14,
chevron: 2.38,
intel: -1.18,
salesforce: -5.88,
},
},
berlin: {
foo: 2,
},
paris: -3,
};
我想要一個函式,讓我們呼叫它removeNegatives()來回傳以下輸出:
// pseudo-code
removeNegatives(stocksMarkets)
// {
// tokyo: {
// today: {
// mitsubishi: 0.65,
// },
// yearToDate: {
// canon: 22.9,
// },
// },
// nyc: {
// sp500: {
// ea: 8.5,
// },
// dowJones: {
// visa: 3.14,
// chevron: 2.38,
// },
// },
// berlin: {
// foo: 2,
// },
// };
這是我的嘗試:
const removeNegatives = (obj) => {
return Object.entries(obj).reduce((t, [key, value]) => {
return {
...t,
[key]:
typeof value !== 'object'
? removeNegatives(value)
: Object.values(value).filter((v) => v >= 0),
};
}, {});
};
但它并沒有真正讓我得到我想要的:-/ 它只在第 2 深度級別上作業(請參閱 參考資料berlin),即使這樣,它也會回傳一個僅包含值而不是完整屬性(即,包括鍵)的陣列。
// { tokyo: [], nyc: [], berlin: [ 2 ], paris: {} }
uj5u.com熱心網友回復:
一個基于 的精益非變異遞回(樹行走)實作Object.entries,Array.prototype.reduce以及一些基本的型別檢查......
function cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(root) {
return Object
.entries(root)
.reduce((node, [key, value]) => {
// simple but reliable object type test.
if (value && ('object' === typeof value)) {
node[key] =
cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(value);
} else if (
// either not a number type
('number' !== typeof value) ||
// OR (if number type then)
// a positive number value.
(Math.abs(value) === value)
) {
node[key] = value;
}
return node;
}, {});
}
const stocksMarkets = {
tokyo: {
today: {
toyota: -1.56,
sony: -0.89,
nippon: -0.94,
mitsubishi: 0.65,
},
yearToDate: {
toyota: -75.95,
softbank: -49.83,
canon: 22.9,
},
},
nyc: {
sp500: {
ea: 8.5,
tesla: -66,
},
dowJones: {
visa: 3.14,
chevron: 2.38,
intel: -1.18,
salesforce: -5.88,
},
},
berlin: {
foo: 2,
},
paris: -3,
};
const rosyOutlooks =
cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(stocksMarkets);
console.log({ rosyOutlooks, stocksMarkets });
.as-console-wrapper { min-height: 100%!important; top: 0; }
由于關于性能的討論正在進行中……許多開發人員仍然低估了 JIT 編譯器對函式陳述句/宣告的性能提升。
我覺得可以隨意搭配r3wt 的性能測驗參考,我可以說的是,兩個帶有 corecursion 的函式陳述句在 chrome/mac 環境中表現最好......
function corecursivelyAggregateEntryByTypeAndValue(node, [key, value]) {
// simple but reliable object type test.
if (value && ('object' === typeof value)) {
node[key] =
cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(value);
} else if (
// either not a number type
('number' !== typeof value) ||
// OR (if number type then)
// a positive number value.
(Math.abs(value) === value)
) {
node[key] = value;
}
return node;
}
function cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(root) {
return Object
.entries(root)
.reduce(corecursivelyAggregateEntryByTypeAndValue, {});
}
const stocksMarkets = {
tokyo: {
today: {
toyota: -1.56,
sony: -0.89,
nippon: -0.94,
mitsubishi: 0.65,
},
yearToDate: {
toyota: -75.95,
softbank: -49.83,
canon: 22.9,
},
},
nyc: {
sp500: {
ea: 8.5,
tesla: -66,
},
dowJones: {
visa: 3.14,
chevron: 2.38,
intel: -1.18,
salesforce: -5.88,
},
},
berlin: {
foo: 2,
},
paris: -3,
};
const rosyOutlooks =
cloneStructureButKeepNumberTypeEntriesOfJustPositiveValues(stocksMarkets);
console.log({ rosyOutlooks, stocksMarkets });
.as-console-wrapper { min-height: 100%!important; top: 0; }
編輯...重構上述基于核心遞回的實作,以涵蓋最新 2 條評論中提到的 2 個開放點...
“OP 在評論中要求能夠通過謂詞函式進行過濾,您的回答沒有這樣做,但盡管如此,我還是將它添加到了作業臺 [...]” – r3wt
“很好(盡管你知道我個人更喜歡簡潔的名字!)我想知道,你為什么選擇 Math.abs(value) === value 而不是 value >= 0?” ——斯科特·索耶
// The implementation of a generic approach gets covered
// by two corecursively working function statements.
function corecursivelyAggregateEntryByCustomCondition(
{ condition, node }, [key, value],
) {
if (value && ('object' === typeof value)) {
node[key] =
copyStructureWithConditionFulfillingEntriesOnly(value, condition);
} else if (condition(value)) {
node[key] = value;
}
return { condition, node };
}
function copyStructureWithConditionFulfillingEntriesOnly(
root, condition,
) {
return Object
.entries(root)
.reduce(
corecursivelyAggregateEntryByCustomCondition,
{ condition, node: {} },
)
.node;
}
// the condition ... a custom predicate function.
function isNeitherNumberTypeNorNegativeValue(value) {
return (
'number' !== typeof value ||
0 <= value
);
}
// the data to work upon.
const stocksMarkets = {
tokyo: {
today: {
toyota: -1.56,
sony: -0.89,
nippon: -0.94,
mitsubishi: 0.65,
},
yearToDate: {
toyota: -75.95,
softbank: -49.83,
canon: 22.9,
},
},
nyc: {
sp500: {
ea: 8.5,
tesla: -66,
},
dowJones: {
visa: 3.14,
chevron: 2.38,
intel: -1.18,
salesforce: -5.88,
},
},
berlin: {
foo: 2,
},
paris: -3,
};
// object creation.
const rosyOutlooks =
copyStructureWithConditionFulfillingEntriesOnly(
stocksMarkets,
isNeitherNumberTypeNorNegativeValue,
);
console.log({ rosyOutlooks, stocksMarkets });
.as-console-wrapper { min-height: 100%!important; top: 0; }
uj5u.com熱心網友回復:
您可以獲取條目并使用正值或嵌套物件收集它們。
const
filterBy = fn => {
const f = object => Object.fromEntries(Object
.entries(object)
.reduce((r, [k, v]) => {
if (v && typeof v === 'object') r.push([k, f(v)]);
else if (fn(v)) r.push([k, v]);
return r;
}, [])
);
return f;
},
fn = v => v >= 0,
filter = filterBy(fn),
stocksMarkets = { tokyo: { today: { toyota: -1.56, sony: -0.89, nippon: -0.94, mitsubishi: 0.65 }, yearToDate: { toyota: -75.95, softbank: -49.83, canon: 22.9 } }, nyc: { sp500: { ea: 8.5, tesla: -66 }, dowJones: { visa: 3.14, chevron: 2.38, intel: -1.18, salesforce: -5.88 } }, berlin: { foo: 2 }, paris: -3 },
result = filter(stocksMarkets);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
uj5u.com熱心網友回復:
注意:這會改變物件。如果不變性是您的用例的一個問題,您應該首先克隆物件。
對于遞回物件節點操作,最好使用提供的訪問者函式創建一個允許遞回“訪問”物件節點的輔助函式,該訪問者函式可以根據業務邏輯自由地操作物件節點。一個基本的實作看起來像這樣(在打字稿中顯示,以便清楚發生了什么):
function visit_object(obj: any, visitor: (o: any, k: string )=>any|void ) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
visit_object(obj[key],visitor);
} else {
visitor(obj,key);//visit the node object, allowing manipulation.
}
}
// not necessary to return obj; js objects are pass by reference value.
}
至于您的具體用例,以下演示如何洗掉值為負的節點。
visit_object( yourObject, (o,k)=>{
if(o[k]<0){
delete o[k];
}
});
編輯:用于性能的咖喱版本,包括可選的 deepClone
const deepClone = (inObject) => {
let outObject, value, key;
if (typeof inObject !== "object" || inObject === null) {
return inObject; // Return the value if inObject is not an object
}
// Create an array or object to hold the values
outObject = Array.isArray(inObject) ? [] : {};
for (key in inObject) {
value = inObject[key];
// Recursively (deep) copy for nested objects, including arrays
outObject[key] = deepClone(value);
}
return outObject;
},
visit_object = visitor => ( obj ) => {
for (let key in obj) {
if (typeof obj[key] === 'object') {
visit_object( obj[key], visitor);
} else {
visitor(obj,key);//visit the node object, allowing manipulation.
}
}
// not necessary to return obj; js objects are pass by reference value.
},
filter=(o,k)=>{
if(o[k]<0){
delete o[k];
}
};
uj5u.com熱心網友回復:
結合Array.prototype.flatMap,Object.entries和Object.fromEntries一定劑量的遞回可以使這樣的問題變得相當簡單:
const removeNegatives = (obj) => Object (obj) === obj
? Object .fromEntries (Object .entries (obj) .flatMap (
([k, v]) => v < 0 ? [] : [[k, removeNegatives (v)]]
))
: obj
const stockMarkets = {tokyo: {today: {toyota: -1.56, sony: -0.89, nippon: -0.94, mitsubishi: 0.65, }, yearToDate: {toyota: -75.95, softbank: -49.83, canon: 22.9}, }, nyc: {sp500: {ea: 8.5, tesla: -66}, dowJones: {visa: 3.14, chevron: 2.38, intel: -1.18, salesforce: -5.88, }, }, berlin: {foo: 2}, paris: -3}
console .log (removeNegatives (stockMarkets))
.as-console-wrapper {max-height: 100% !important; top: 0}
如果我們的輸入不是一個物件,我們就原封不動地回傳它。如果是,我們將其拆分為鍵值對,然后對于其中的每一個,如果值為負數,則跳過它;否則我們會重復該值。然后我們將這些生成的鍵值對拼接回一個物件。
您可能想要對vbefore進行型別檢查v < 0。這是你的電話。
不過,這是在乞求更高層次的抽象。我可能更喜歡這樣寫:
const filterObj = (pred) => (obj) => Object (obj) === obj
? Object .fromEntries (Object .entries (obj) .flatMap (
([k, v]) => pred (v) ? [[k, filterObj (pred) (v)]] : []
))
: obj
const removeNegatives = filterObj ((v) => typeof v !== 'number' || v > 0)
更新:簡單的葉子過濾
OP 要求一種允許對葉子進行更簡單過濾的方法。我知道的最簡單的方法是經歷這樣的中間階段:
[
[["tokyo", "today", "toyota"], -1.56],
[["tokyo", "today", "sony"], -0.89],
[["tokyo", "today", "nippon"], -0.94],
[["tokyo", "today", "mitsubishi"], 0.65],
[["tokyo", "yearToDate", "toyota"], -75.95],
[["tokyo", "yearToDate", "softbank"], -49.83],
[["tokyo", "yearToDate", "canon"], 22.9],
[["nyc", "sp500", "ea"], 8.5],
[["nyc", "sp500", "tesla"], -66],
[["nyc", "dowJones", "visa"], 3.14],
[["nyc", "dowJones", "chevron"], 2.38],
[["nyc", "dowJones", "intel"], -1.18],
[["nyc", "dowJones", "salesforce"], -5.88],
[["berlin", "foo"], 2],
[["paris"], -3]
]
然后對這些條目運行我們的簡單過濾器以獲得:
[
[["tokyo", "today", "mitsubishi"], 0.65],
[["tokyo", "yearToDate", "canon"], 22.9],
[["nyc", "sp500", "ea"], 8.5],
[["nyc", "dowJones", "visa"], 3.14],
[["nyc", "dowJones", "chevron"], 2.38],
[["berlin", "foo"], 2],
]
并將其重新組合成一個物件。我在執行提取和補液的功能周圍存在,所以實際上只需將它們捆綁在一起即可:
// utility functions
const pathEntries = (obj) =>
Object (obj) === obj
? Object .entries (obj) .flatMap (
([k, x]) => pathEntries (x) .map (([p, v]) => [[Array.isArray(obj) ? Number(k) : k, ... p], v])
)
: [[[], obj]]
const setPath = ([p, ...ps]) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number.isInteger (p) ? [] : {},
{...o, [p]: setPath (ps) (v) ((o || {}) [p])}
)
const hydrate = (xs) =>
xs .reduce ((a, [p, v]) => setPath (p) (v) (a), {})
const filterLeaves = (fn) => (obj) =>
hydrate (pathEntries (obj) .filter (([k, v]) => fn (v)))
// main function
const removeNegatives = filterLeaves ((v) => v >= 0)
// sample data
const stockMarkets = {tokyo: {today: {toyota: -1.56, sony: -0.89, nippon: -0.94, mitsubishi: 0.65, }, yearToDate: {toyota: -75.95, softbank: -49.83, canon: 22.9}, }, nyc: {sp500: {ea: 8.5, tesla: -66}, dowJones: {visa: 3.14, chevron: 2.38, intel: -1.18, salesforce: -5.88, }, }, berlin: {foo: 2}, paris: -3}
// demo
console .log (removeNegatives (stockMarkets))
.as-console-wrapper {max-height: 100% !important; top: 0}
您可以在各種其他答案中查看pathEntries、setPath和的詳細資訊。這里的重要功能是,它只接受葉值的謂詞,運行,使用該謂詞過濾結果并呼叫結果。這使得我們的主要功能變得微不足道,.hydratefilterLeavespathEntrieshydratefilterLeaves ((v) => v > 0)
但是我們可以用這個故障做各種各樣的事情。我們可以根據鍵和值進行過濾。我們可以在補水之前過濾和映射它們。我們甚至可以映射到多個新的鍵值結果。這些可能性中的任何一種都像這樣簡單filterLeaves。
uj5u.com熱心網友回復:
用這個:
const removeNegatives = (obj) => {
return Object.entries(obj).reduce((t, [key, value]) => {
const v =
value && typeof value === "object"
? removeNegatives(value)
: value >= 0
? value
: null;
if (v!==null) {
t[key] = v;
}
return t;
}, {});
};
const stocksMarkets = {
tokyo: {
today: {
toyota: -1.56,
sony: -0.89,
nippon: -0.94,
mitsubishi: 0.65,
},
yearToDate: {
toyota: -75.95,
softbank: -49.83,
canon: 22.9,
},
},
nyc: {
sp500: {
ea: 8.5,
tesla: -66,
},
dowJones: {
visa: 3.14,
chevron: 2.38,
intel: -1.18,
salesforce: -5.88,
},
},
berlin: {
foo: 2,
},
paris: -3,
};
const positives = removeNegatives(stocksMarkets);
console.log({ positives, stocksMarkets });
.as-console-wrapper { min-height: 100%!important; top: 0; }
uj5u.com熱心網友回復:
它不必那么復雜。只需遍歷物件。如果當前屬性是一個物件,則使用該物件再次呼叫該函式,否則檢查該屬性值是否小于 0,如果是,則將其洗掉。最后回傳更新的物件。
const stocksMarkets={tokyo:{today:{toyota:-1.56,sony:-.89,nippon:-.94,mitsubishi:.65},yearToDate:{toyota:-75.95,softbank:-49.83,canon:22.9}},nyc:{sp500:{ea:8.5,tesla:-66},dowJones:{visa:3.14,chevron:2.38,intel:-1.18,salesforce:-5.88}},berlin:{foo:2},paris:-3};
function remove(obj) {
for (const prop in obj) {
if (typeof obj[prop] === 'object') {
remove(obj[prop]);
continue;
}
if (obj[prop] < 0) delete obj[prop];
}
return obj;
}
console.log(remove(stocksMarkets));
注意:這會改變原始物件。如果您想制作該物件的深層副本并使用它來代替最快的方法可能是字串化,然后決議它。
const copy = JSON.parse(JSON.stringify(stocksMarkets));
或者:在一些瀏覽器中有一個全新的特性叫做structuredClone(查看兼容性表)。
const clone = structureClone(stocksMarkets);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/473271.html
標籤:javascript 递归 筛选 对象字面量
