I have my current ugly code to filter an array with specific requirements, is there any better ways to simplify the code by using arrow function or such in order to beautify and shorten the code? tks.
const NewTraitSet = [
{
"ValueSet": [
{
"Units": null,
"Type": "description A",
"Value": "description A"
},
{
"Units": null,
"Type": "description B",
"Value": "description B"
},
{
"Units": null,
"Type": "risk category",
"Value": "Decreased Risk"
}
],
"TraitInterpretation": "TraitInterpretation",
"TraitCategory": "Health"
},{
"ValueSet": [
{
"Units": null,
"Type": "description A",
"Value": "description A"
},
{
"Units": null,
"Type": "description B",
"Value": "description B"
},
{
"Units": null,
"Type": "risk category",
"Value": "Increased Risk"
}
],
"TraitInterpretation": "TraitInterpretation",
"TraitCategory": "DD Health",
}
]
const result = []
function fun(item, index) {
for (var key in item) {
if (key == "ValueSet") {
for (var obj in item[key]) {
if (item[key][obj]["Type"] == "risk category" && item[key][obj]["Value"] == "Decreased Risk") {
result.push(item)
}
}
}
}
return result
}
NewTraitSet.forEach(fun)
// sort traits by name
result.sort((a, b) => (a.TraitCategory > b.TraitCategory ? 1 : -1))
console.log(result)
I'm pretty new to javascript and not so sure how to use arrow function in my case, some advices will be brilliant. Or if there is better solution to get the same result, I will be more than happy to learn from it, tks.
Because your output array is composed of elements of your input array, use .filter. Since you're also only concerned with the ValueSet key, you only need to check that key, you don't need to iterate over all of an object's keys.
Then, you need to check if any of the elements in the array passes a test, and if so, push the whole object. The right method to perform such a test is .some:
const NewTraitSet=[{ValueSet:[{Units:null,Type:"description A",Value:"description A"},{Units:null,Type:"description B",Value:"description B"},{Units:null,Type:"risk category",Value:"Decreased Risk"}],TraitInterpretation:"TraitInterpretation",TraitCategory:"Health"},{ValueSet:[{Units:null,Type:"description A",Value:"description A"},{Units:null,Type:"description B",Value:"description B"},{Units:null,Type:"risk category",Value:"Increased Risk"}],TraitInterpretation:"TraitInterpretation",TraitCategory:"DD Health"}];
const result = NewTraitSet.filter(item => item.ValueSet.some(
({ Type, Value }) => Type === 'risk category' && Value === 'Decreased Risk'
));
console.log(result);
Or, if you're not comfortable with destructuring:
const NewTraitSet=[{ValueSet:[{Units:null,Type:"description A",Value:"description A"},{Units:null,Type:"description B",Value:"description B"},{Units:null,Type:"risk category",Value:"Decreased Risk"}],TraitInterpretation:"TraitInterpretation",TraitCategory:"Health"},{ValueSet:[{Units:null,Type:"description A",Value:"description A"},{Units:null,Type:"description B",Value:"description B"},{Units:null,Type:"risk category",Value:"Increased Risk"}],TraitInterpretation:"TraitInterpretation",TraitCategory:"DD Health"}];
const result = NewTraitSet.filter(item => item.ValueSet.some(
inner => inner.Type === 'risk category' && inner.Value === 'Decreased Risk'
));
console.log(result);
I used your code with some annotations.
const
newTraitSet = [{ ValueSet: [{ Units: null, Type: "description A", Value: "description A" }, { Units: null, Type: "description B", Value: "description B" }, { Units: null, Type: "risk category", Value: "Decreased Risk" }], TraitInterpretation: "TraitInterpretation", TraitCategory: "Health" }, { ValueSet: [{ Units: null, Type: "description A", Value: "description A" }, { Units: null, Type: "description B", Value: "description B" }, { Units: null, Type: "risk category", Value: "Increased Risk" }], TraitInterpretation: "TraitInterpretation", TraitCategory: "DD Health" }],
result = [];
function fun(item) { // remove unused variables
//for (var key in item) { // remove this, because you need only a single property
if (!("ValueSet" in item)) return; // return early, if "ValueSet" not exists in rhe object
// if (key == "ValueSet") {// remove check, because no iteration over the keys
for (let obj of item.ValueSet) { // use `of` for the values and key directly
if (obj.Type == "risk category" && obj.Value == "Decreased Risk") { // dot notation for known property
result.push(item);
}
}
//}
//}
// return result; // makes no sense in `forEach`. the return value is never used
}
newTraitSet.forEach(fun);
result.sort((a, b) => a.TraitCategory.localeCompare(b.TraitCategory ? 1 : -1)); // sort by strings
console.log(result);
Related
I have an object which contains questions with different types multiple, single and text.
const data = {
questions: [
{
"question": "Question 1",
"value": [
"value_1.1.1",
"value_1.1.2"
],
"type": "multiple",
"options": [
{
"value": "value_1.1.1",
"label": "Value 1.1.1"
},
{
"textValue":"Additional text value",
"value": "value_1.1.2",
"label": "Value 1.1.2"
},
{
"value":"value_1.1.3",
"label":"Value 1.1.3",
}
]
},
{
"question": "Question 2",
"value": "value_2.1.1",
"type": "single",
"options": [
{
"value": "value_2.1.1",
"label": "Value 2.1.1"
},
{
"value": "value_2.1.2",
"label": "Value 2.1.2"
},
]
},
{
"question":"Question 3",
"textValue":"Test 12345",
"type":"text"
}
]
}
I want to create a new array with objects, which contains the question key and the value key.
The value key must contain the label of the selected options.
For a multiple type, the value can contain multiple labels.
The selected option can be retrieved from the parent value key, if it is equal to the value of the option, I want to use the label of this option as value.
In some cases an option also contains a textValue (see 1.1.3), then this must also be added to the value.
So the new array should look like this for the example above:
const newData = [
{
question: "Question 1",
value: ['Value 1.1.1', 'Value 1.1.2', 'Additional text value']
},
{
question: "Question 2",
value: ['Value 2.1.1']
},
{
question: "Question 3",
value: ['Test 12345']
}
];
Well basically loop over the questions, handling each by its own type. The tricky part is the type: multiple but it is managable.
Update: Fixed according to comment
const data = {questions:[{question:"Question 1",value:["value_1.1.1","value_1.1.2"],type:"multiple",options:[{value:"value_1.1.1",label:"Value 1.1.1"},{value:"value_1.1.2",label:"Value 1.1.2"},{textValue:"test12",value:"value_1.1.3",label:"Value 1.1.3"}]},{question:"Question 2",value:"value_2.1.1",type:"single",options:[{value:"value_2.1.1",label:"Value 2.1.1"},{value:"value_2.1.2",label:"Value 2.1.2"},]},{question:"Question 3",textValue:"Test 12345",type:"text"}]};
function get_option_label(options, value) {
return options.find(item => item.value == value).label || value
}
const newData = [];
data.questions.forEach(function(obj) {
var new_obj = {
question: obj.question
}
if (obj.type == "multiple") {
new_obj.value = JSON.parse(JSON.stringify(obj.value))
new_obj.value = new_obj.value.map(item => get_option_label(obj.options, item))
// add textValue for multiple question
// but only if its value is on parent
var objText = obj.options.find((item) => item.textValue);
if (objText && obj.value.indexOf(objText.value) >= 0) {
new_obj.value.push(objText.textValue)
}
}
if (obj.type == "single") {
new_obj.value = [get_option_label(obj.options, obj.value)]
}
if (obj.type == "text") {
new_obj.value = [obj.textValue]
}
newData.push(new_obj)
})
// now replacing all values with labels
console.log(newData);
.as-console-wrapper {
max-height: 100% !important;
top: 0
}
there is a list of users
filterData = [
{
"position":"lawyer",
"department_positions":[],
"group_positions":[
{"group":{"id":2,"code":"234","name":"group1"},"lead":false},
{"group":{"id":1,"code":"123","name":"group12"},"lead":true}
]
},
{
"position":"director",
"department_positions":[
{"department":{"id":3,"code":"333","name":"subDep"},"lead":false}
],
"group_positions":[
{"group":{"id":2,"code":"234","name":"group1"},"lead":false},
{"group":{"id":1,"code":"123","name":"group12"},"lead":true}
]
},
{
"position":"director",
"department_positions":[],
"group_positions":[]
}
]
and list of filters
categories = {
"position":["lawyer","director"],
"group_positions":["group1","group12"],
"department_positions":["generalDep", "subDep"]
}
It is necessary to filter users taking into account the fact that several filters can be selected at the same time. For example, i want to find user with position = "director" and AND group_positions = "group1" AND department_positions = "subDep"
my code doesn't allow filtering by multiple conditions. how can i fix it?
this.filter = this.filterData.filter(item => {
for (let key in this.categories) {
if (item[key].find(el =>
this.categories[key].includes(
el.group?.name || el.department?.name
)
)) {
return true
}
}
return false
})}
This is a good place to employ an es6 class to give behavior to the object being filtered. Augment each object to determine if it matches the "category" object.
(from the example data, this assumes the OP is looking for a "product of sums" match: for all of the category keys match at least one of the category values)
class FilterMe {
constructor(item) {
Object.assign(this, item);
}
namesForKey(key) {
switch (key) {
case 'position':
return [this.position]; // always answer an array
case 'group_positions':
return this.group_positions.map(gp => gp.group.name);
case 'department_positions':
return this.department_positions.map(dp => dp.department.name);
default:
return [];
}
}
// return true if a single filter key-value pair is matched
matchesFilterKeyValue(filterKey, filterOptions) {
const myNames = this.namesForKey(filterKey);
const matches = filterOptions.filter(e => myNames.includes(e));
return matches.length > 0;
}
// return true if all filter key-values pairs are matched
matchesFilter(filter) {
return Object.entries(filter).every(keyValue => {
return this.matchesFilterKeyValue(...keyValue);
})
}
}
const filterData = [{
"position": "lawyer",
"department_positions": [],
"group_positions": [{
"group": {
"id": 2,
"code": "234",
"name": "group1"
},
"lead": false
}, {
"group": {
"id": 1,
"code": "123",
"name": "group12"
},
"lead": true
}]
},
{
"position": "director",
"department_positions": [{
"department": {
"id": 3,
"code": "333",
"name": "subDep"
},
"lead": false
}],
"group_positions": [{
"group": {
"id": 2,
"code": "234",
"name": "group1"
},
"lead": false
}, {
"group": {
"id": 1,
"code": "123",
"name": "group12"
},
"lead": true
}]
},
{
"position": "director",
"department_positions": [],
"group_positions": []
}
]
const categories = {
"position": ["lawyer", "director"],
"group_positions": ["group1", "group12"],
"department_positions": ["generalDep", "subDep"]
}
// convert the filterData to the objects and test them...
let objects = filterData.map(d => new FilterMe(d));
let matches = objects.filter(o => o.matchesFilter(categories))
console.log(matches)
You can try something like this:
let filtered = example.filter(item => {
let valid = false
if (item.includes('something')) {
valid = true
}
if (!valid) {
// check second condition
}
return valid
})
Use a temporary placeholder so you don't immediately have to return true/false.
I am fetching a JSON from an endpoint and I would like to insert it into my mongoDB database. The problem is that some keys that comes from the JSON have a "." inside it and mongoDB throws an error when it suppose to insert this kind of key, for example:
object: {
"DR.No": 1
}
This is the actual endpoint I am fetching:
https://api.opensea.io/api/v1/asset_contracts/
It is an array of objects, some of them have a "traits" object which sometimes has these kind of key-value (Ctrl+F and search for "DR." to see what I mean).
When I deleted this section everything worked well.
app.post("/something", (req, res, next) => {
fetch("https://api.opensea.io/api/v1/asset_contracts/")
.then(r => r.json())
.then(data => {
for (let i = 0; i < data.length; i++) {
delete data[i].traits; //works when deleted, I need it though
...
I need the traits section. I would like to replace all of the dots with commas.
Therefore I need to change the key's and its children keys name dynamically.
Similar questions focus more on how to include the . part in mongoDB than to dynamically change the key (this is the solution I desire)
One possible way to do it is to re write the keys into a new object like this:
let obj = {};
let o = { "DR.No":1, "foo": 2 };
let keys = Object.keys(o);
for(let i = 0;i < keys.length;i++) {
let key = keys[i];
obj[key.replace(".",",")] = o[key]
}
JSON.stringify(obj,null,2)
"{
"DR,No": 1,
"foo": 2
}"
Or if there are many dots:
let obj = {};
let o = { "DR.No.Blah.Dots.ToMany":1, "Weird.Dots.In.Keys": 2 };
let keys = Object.keys(o);
for(let i = 0;i < keys.length;i++) {
let key = keys[i];
let originalKey = key;
key = key.split(".").join(",");
obj[key] = o[originalKey]
}
JSON.stringify(obj,null,2)
"{
"DR,No,Blah,Dots,ToMany": 1,
"Weird,Dots,In,Keys": 2
}"
If you want the keys to not have punctuation in them get rid of the commas....
....in answer to your comment here is one possible way to do it. Taking a sample from the json data in your question:
var data = [
{
"address": "0xde083e40fe84835cbbd6c69f6595cae1e85551dc",
"name": "Ledger Legend Cards",
"symbol": "LLC",
"image_url": "https://storage.googleapis.com/opensea-static/ledgerlegends-logo.png",
"featured_image_url": null,
"featured": false,
"description": "Ledger Legends is an early access collectible card game built on the Ethereum platform. It uses the new ERC721 non-fungible token standard to track its cards. Using this standard makes it easy for the cards to integrate into the wider Ethereum ecosystem, like exchanges and wallets. Being a card game that is built using smart contracts you know that your cards will always be owned by you and can never be taken away by anyone unlike centralized games such as Hearthstone.",
"external_link": "https://ledgerlegends.com/",
"wiki_link": null,
"stats": {
"seven_day_volume": 0,
"seven_day_change": 0,
"total_volume": 0,
"count": 282,
"num_owners": 54,
"market_cap": 0,
"average_price": 0,
"items_sold": 0
},
"traits": [
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
"MORE.MORE.MORE": [
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
},
{
"FN.RA.NA": "Value A",
"RR.TT.DD": "Value B",
}
]
}
],
"hidden": true,
"nft_version": "1.0",
"schema_name": "ERC721",
"display_data": {
"images": [
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png"
]
},
"short_description": null,
"total_supply": null,
"owner": null,
"buyer_fee_basis_points": 0,
"seller_fee_basis_points": 250
}
];
//is it an object
function isObject(o) {
return Object.prototype.toString.call(o) === "[object Object]";
}
//is it an array
function isArray(o) {
return Array.isArray(o);
}
//clean the keys and take advantage of the reference to the original
//object to re write and replace the keys and their values
function cleanKeys(o) {
var keys = Object.keys(o);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var toCheck = o[key];
var originalKey = key;
//if there is a dot in the key
//re write it and replace it
if (key.indexOf('.') > -1) {
key = key.split(".").join(",");
o[key] = o[originalKey];
delete o[originalKey];
}
if (isArray(toCheck) || isObject(toCheck)) {
removeDots(toCheck);
}
}
}
//a somewhat recursive function with bits broken out for readability
function removeDots(obj) {
switch (Object.prototype.toString.call(obj)) {
case "[object Array]":
for (var i = 0; i < obj.length; i++) {
var o = obj[i];
if (isArray(o)) {
removeDots(obj);
} else {
cleanKeys(o);
}
}
break;
case "[object Object]":
cleanKeys(obj);
break;
}
}
removeDots(data);
console.log(JSON.stringify(data, null, 2));
[
{
"address": "0xde083e40fe84835cbbd6c69f6595cae1e85551dc",
"name": "Ledger Legend Cards",
"symbol": "LLC",
"image_url": "https://storage.googleapis.com/opensea-static/ledgerlegends-logo.png",
"featured_image_url": null,
"featured": false,
"description": "Ledger Legends is an early access collectible card game built on the Ethereum platform. It uses the new ERC721 non-fungible token standard to track its cards. Using this standard makes it easy for the cards to integrate into the wider Ethereum ecosystem, like exchanges and wallets. Being a card game that is built using smart contracts you know that your cards will always be owned by you and can never be taken away by anyone unlike centralized games such as Hearthstone.",
"external_link": "https://ledgerlegends.com/",
"wiki_link": null,
"stats": {
"seven_day_volume": 0,
"seven_day_change": 0,
"total_volume": 0,
"count": 282,
"num_owners": 54,
"market_cap": 0,
"average_price": 0,
"items_sold": 0
},
"traits": [
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B",
"MORE,MORE,MORE": [
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
},
{
"FN,RA,NA": "Value A",
"RR,TT,DD": "Value B"
}
]
}
],
"hidden": true,
"nft_version": "1.0",
"schema_name": "ERC721",
"display_data": {
"images": [
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png",
"https://ledgerlegends.com/img/monster.png"
]
},
"short_description": null,
"total_supply": null,
"owner": null,
"buyer_fee_basis_points": 0,
"seller_fee_basis_points": 250
}
]
maybe
data[i].traits = Object.entries(data[i].traits).reduce((memo, [key, value]) => {
memo[key.replace(/./g, ',')] = value;
return memo;
}, {});
I want to return true if all the product contain Done in the statusLog array. If any product does not contain Done in the statusLog then it should return false. Some product may not have statusLog property which mean it should return false.
The code seem to work fine, but I felt there must be a better way to refactor this. If "Done" not found from the first product then it should skip the loop as it no need to keep looping. How can that be done?
data={"id":123,"products":[{"id":1,"name":"Item 1","statusLog":[{"name":"New"},{"name":"Done"}]},{"id":2,"name":"Item 2","statusLog":[{"name":"New"},{"name":"Done"}]},{"id":3,"name":"Item 3","statusLog":[{"name":"Pending"},{"name":"Dones"}]},]}
var hasDone = 0;
data.products.forEach((product) => {
if (product.statusLog) {
if (product.statusLog.some((status) => {
return status.name == "Done"
})) {
hasDone++
}
}
});
if (hasDone != data.products.length) {
console.log("All products has Done Status")
}
Demo: https://jsfiddle.net/bdrve3xs/18/
You can use Array.prototype.every
const allHaveDone = data.products.every(
product => (product.statusLog || []).some( status => status.name === "Done" )
);
if ( allHaveDone ) {
console.log("All products has Done Status")
}
You could use filter and check its length.
When done like this, you also get the "not done" ones in return.
Stack snippet
data = {
"id": 123,
"products": [{
"id": 1,
"name": "Item 1",
"statusLog": [{ "name": "New" }, { "name": "Done" }]
},
{
"id": 2,
"name": "Item 2",
"statusLog": [{ "name": "New" }, { "name": "Done" }]
},
{
"id": 3,
"name": "Item 3",
"statusLog": [{ "name": "Pending" }, { "name": "Dones" }]
},
]
}
var not_done = data.products.filter(p => {
if (!p.statusLog || !p.statusLog.some(s => s.name === "Done")) return p;
});
if (not_done.length > 0) {
console.log('These/This is not "Done" yet', not_done)
}
I have some code that will return me the name of the subcat clicked.
Here is the data below:
theData = {
"categories": [
{
"id": "661",
"name": "some name",
"description": "some description",
"subcat": [
{
"id": "662",
"name": "sub 1",
"translations": null
},
{
"id": "663",
"name": "sub 2",
"translations": null
}
],
"image": null
},
{
"id": "657",
"name": "some other name",
"description": "",
"subcat": [
{
"id": "456",
"name": "sub 12",
"translations": null
},
{
"id": "656",
"name": "sub 15",
"translations": null
}
],
"image": null
}
]
};
I need some way to find the parent id of the subcat name.
For example if I gave it "sub 15", it would return "661" which is the parent id
How can I do this?
There's no way by default to access the "parent" of an object in Javascript -- it could be referenced in any number of other objects, or even by itself, so it's not possible to determine what the sole parent of an object is.
Instead, we'll just iterate through all the data until we find the matching id, and return null if we never find it. This solution counts on your IDs being unique entities, so if that's not the case, it'll have to be changed. With that said, here's an example:
function getParent(subID) {
for (var i in theData.categories) {
var parent = theData.categories[i];
if ('subcat' in parent && 'length' in parent.subcat) {
for (var j = 0; j < parent.subcat.length; j++) {
if (parent.subcat[j].id === subID) {
return parent.id;
}
}
}
}
return null;
}
If you don't like your function returning null, you can always alter it so that it returns -1, assuming that -1 is out-of-band for your IDs. Note also that this is hardly an optimal solution, so if you're intending to use this for large amounts of data, you'll want to look into faster and/or more efficient search algorithms.
try this .we have to iterate through all the data
[fiddle][1]
Fiddle
var input = "sub 15";
var id =-1;
for(var i=0;i<theData.categories.length;i++){
if(theData.categories[i].subcat!=null){
for(var j=0;j<theData.categories[i].subcat.length;j++){
if(theData.categories[i].subcat[j].name==input){
id= theData.categories[i].id;
break;
}
}
}
}
console.log(id)
You might consider more a generic solution, which searches for a given key and a value in the subcat array. Then take only the first object's id.
function getParentId(key, value) {
return (theData.categories.filter(function (a) {
return a.subcat.some(function (b) {
return b[key] === value;
});
})[0] || {}).id;
}
var theData = { categories: [{ id: 661, name: "some name", description: "some description", subcat: [{ id: 662, name: "sub 1", translations: null }, { id: 663, name: "sub 2", translations: null }], image: null }, { id: 657, name: "some other name", description: 0, subcat: [{ id: 456, name: "sub 12", translations: null }, { id: 656, name: "sub 15", translations: null }], image: null }] };
console.log(getParentId('name', 'sub 15'));
This may not be the best way but hope this will be useful
var getSubCat = []; // create an empty array to map parent id with sub name
var getCat = theData.categories; // get categories
getCat.forEach(function(item) { // loop through categories
item.subcat.forEach(function(elem) {
getSubCat.push({
id: item.id, // this is parent id
sub: elem.name // name inside each subcat object. Total 4 subcat object
});
})
})
var parentId = getSubCat.filter(function(elem){ // filter the getSubCat array
return elem.sub ==='sub 15'
})[0].id // parentId is an array, we need first object so [0]
alert(parentId)
Now I am pushing name inside each of subcat object. On requirement you can also push their id
DEMO
The below solution looks similar to furkle's solution. But here it uses jquery to iterate through the json objects.
function getParent(subID) {
var parentId = null;
$.each(theData.categories, function(i, v)
{
$.each(v.subcat, function(idx, obj){
if(obj.name ==subID)
parentId = v.id
})
})
return parentId;
}