I have the below JSON which controls the flow of the pages in my application. To navigate between screens manually I use the index of a page. If I were to link to Page 1 I would use the index '0'. I would like to be able to navigate using the id instead. So I could use S010_010 to navigate. Unfortunately the navigation function of the app is used by multiple elements and cannot be changed completely so I am looking for a method that can take the id and return the index from flows.
var flows = {
"default": [{
theme: "theme1",
events: "touchstart",
easingSlideIn: "transition.fadeIn",
easingSlideOut: "transition.fadeOut",
easingRef: "transition.perspectiveRightIn",
easingPop: "transition.flipYIn"
},
{
id: "S010_010", //0
description: "",
chapter: "01",
ref: ""
},
{
id: "S020_010", //1
description: "",
chapter: "01",
ref: ""
},
{
id: "S030_010", //2
description: "",
chapter: "01",
ref: ""
},
]
};
This is an example of how I currently retrieve the id using the index:
this.options.flow[screen +1].id
You can use for-in to iterate and return the index if id matches with that of method's. It is also recommended to use for-of but you end up descructing something like this for(const [index, value] of flows.default.entries()) to fetch index hence used for-in
let flows = {
"default": [{
theme: "theme1",
events: "touchstart",
easingSlideIn: "transition.fadeIn",
easingSlideOut: "transition.fadeOut",
easingRef: "transition.perspectiveRightIn",
easingPop: "transition.flipYIn"
},
{
id: "S010_010", //0
description: "",
chapter: "01",
ref: ""
},
{
id: "S020_010", //1
description: "",
chapter: "01",
ref: ""
},
{
id: "S030_010", //2
description: "",
chapter: "01",
ref: ""
},
]
};
let getFlowByID = (id) => {
for(let eachFlowIndex in flows.default){
if(flows.default[eachFlowIndex].id == id){
return eachFlowIndex;
}
}
}
console.log(getFlowByID("S030_010")); // gets S030_010 index
There is no specific method, but you can create your own inverted index
var invertedIndex = {};
flows.default.forEach((elem, index) => {
if(elem.id != null) {
invertedIndex[elem.id] = index - 1;
}
})
//then you can use the id for the lookup as
invertedIndex['S030_010']
We use for in cycle to iterate through our array of objects and return the index if id was found.
ES5 solution
var flows =
{
"default":
[
{
theme: "theme1",
events: "touchstart",
easingSlideIn: "transition.fadeIn",
easingSlideOut: "transition.fadeOut",
easingRef: "transition.perspectiveRightIn",
easingPop: "transition.flipYIn"
},
{
id: "S010_010", //0
description: "",
chapter: "01",
ref: ""
},
{
id: "S020_010", //1
description: "",
chapter: "01",
ref: ""
},
{
id: "S030_010", //2
description: "",
chapter: "01",
ref: ""
}
]
};
function getIndex(id)
{
var i = 0,
objs = flows.default;
for (i in objs)
if (objs[i].id == id)
return +i - 1
}
console.log(getIndex('S010_010')); //0
ES6 solution
We use Array.find function to iterate through our array, save the index if id was found and than return it. In the case if it is not found it returns -1. But unfortunatelly this solution isn't shorter than my ES5 solution.
var flows =
{
"default":
[
{
theme: "theme1",
events: "touchstart",
easingSlideIn: "transition.fadeIn",
easingSlideOut: "transition.fadeOut",
easingRef: "transition.perspectiveRightIn",
easingPop: "transition.flipYIn"
},
{
id: "S010_010", //0
description: "",
chapter: "01",
ref: ""
},
{
id: "S020_010", //1
description: "",
chapter: "01",
ref: ""
},
{
id: "S030_010", //2
description: "",
chapter: "01",
ref: ""
}
]
};
let getIndex = (id) => {
let ret = -1;
flows.default.find((elem, index) => {
if(elem.id == id)
ret = index - 1
});
return ret
};
console.log(getIndex('S010_010')); //0
Related
I have a json tree structure that I want to normalize into something like a hashmap and then denormalize it back to a tree if needed.
I have a very dynamic tree that I want to use as state in my react-redux project, but for that I somehow need to transform the data so that I can access it without having to search elements recursively in the tree each time I want to update/access the state.
const actual = {
id: "1",
type: 'Container',
data: {},
items: [
{
id: "2",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
},
{
id: "3",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
}
]
};
Now I want to transform it into something like:
const expected = {
1: {
id: "1",
type: 'Container',
data: {},
items: [1, 2]
},
2: {
id: "2",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
},
3: {
id: "3",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
}
};
I found a JS lib called Normalizr, but I absolutely don't get how to create the schemas for it to work.
That was my last try, and it returns only the inner two items and also directly the data object inside without id, items around:
const data = new schema.Entity("data");
const item = new schema.Object({ data });
item.define({ items: new schema.Array(item) });
const items = new schema.Array(item);
const normalizedData = normalize(mock, items);
I'm not going to worry too much about the types, since you can alter those to meet your needs. Going off you're example, I will define
interface Tree {
id: string;
type: string;
data: {
title?: string;
text?: string;
items: Tree[];
}
}
interface NormalizedTree {
[k: string]: {
id: string;
type: string;
data: {
title?: string;
text?: string;
items: string[]
}
}
}
and we want to implement function normalize(tree: Tree): NormalizedTree and function denormalize(norm: NormalizedTree): Tree.
The normalize() function is fairly straightforward since you can recursively walk the tree and collect the normalized trees into one big normalized tree:
function normalize(tree: Tree): NormalizedTree {
return Object.assign({
[tree.id]: {
...tree,
data: {
...tree.data,
items: tree.data.items.map(v => v.id)
}
},
}, ...tree.data.items.map(normalize));
}
In English, we are making a single normalized tree with a property with key tree.id and a value that's the same as tree except the data.items property is mapped to just the ids. And then we are mapping each element of data.items with normalize to get a list of normalized trees that we spread into that normalized tree via the Object.assign() method. Let's make sure it works:
const normalizedMock = normalize(mock);
console.log(normalizedMock);
/* {
"1": {
"id": "1",
"type": "Container",
"data": {
"items": [
"2",
"3"
]
}
},
"2": {
"id": "2",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
},
"3": {
"id": "3",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
}
} */
Looks good.
The denormalize() function is a little trickier, because we need to trust that the normalized tree is valid and actually represents a tree with a single root and no cycles. And we need to find and return that root. Here's one approach:
function denormalize(norm: NormalizedTree): Tree {
// make Trees with no children
const treeHash: Record<string, Tree> =
Object.fromEntries(Object.entries(norm).
map(([k, v]) => [k, { ...v, data: { ...v.data, items: [] } }])
);
// keep track of trees with no parents
const parentlessTrees =
Object.fromEntries(Object.entries(norm).map(([k, v]) => [k, true]));
Object.values(norm).forEach(v => {
// hook up children
treeHash[v.id].data.items = v.data.items.map(k => treeHash[k]);
// trees that are children do have parents, remove from parentlessTrees
v.data.items.forEach(k => delete parentlessTrees[k]);
})
const parentlessTreeIds = Object.keys(parentlessTrees);
if (parentlessTreeIds.length !== 1)
throw new Error("uh oh, there are " +
parentlessTreeIds.length +
" parentless trees, but there should be exactly 1");
return treeHash[parentlessTreeIds[0]];
}
In English... first we copy the normalized tree into a new treeHash object where all the data.items are empty. This will eventually hold our denormalized trees, but right now there are no children.
Then, in order to help us find the root, we make a set of all the ids of the trees, from which we will remove any ids corresponding to trees with parents. When we're all done, there should hopefully be a single id left, that of the root.
Then we start populating the children of treeHash's properties, by mapping the corresponding data.items array from the normalized tree to an array of properties of treeHash. And we remove all of these child ids from parentlessTreeIds.
Finally, we should have exactly one property in parentlessTreeIds. If not, we have some kind of forest, or cycle, and we throw an error. But assuming we do have a single parentless tree, we return it.
Let's test it out:
const reconsitutedMock = denormalize(normalizedMock);
console.log(reconsitutedMock);
/* {
"id": "1",
"type": "Container",
"data": {
"items": [
{
"id": "2",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
},
{
"id": "3",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
}
]
}
} */
Also looks good.
Playground link to code
I would recommend .flatMap for this kind of transformations:
const flattenTree = element => [
element,
...element.data.items.flatMap(normalizeTree)
]
This move you from this shape:
{
id: 1,
data: { items: [
{
id: 2,
data: { items: [
{ id: 3, data: { items: [] } },
] }
] }
}
to this one:
[
{ id: 1, data: {...}},
{ id: 2, data: {...}},
{ id: 3, data: {...}},
]
Then once you have a flat array, you can transform it further to remove the references and create an object from entries:
const normalizedTree = element => {
let flat = flattenTree(element)
// only keep the id of each items:
// [{ id: 1, data:{...}}] -> [1]
// for (const el of flat) {
// el.data.items = el.data.items.map(child => child.id)
// }
// note that the for loop will change the initial data
// to preserve it you can achieve the same result with
// a map that will copy every elements:
const noRefs = flat.map(el => ({
...el,
data: {
...el.data,
items: el.data.items.map(child => child.id),
},
}))
// then if you need an object, 2 steps, get entries, [id, elem]:
const entries = noRefs.map(({ id, ...element }) => [id, element])
// then the built-in `Object.fromEntries` do all the work for you
// using the first part of the entry as key and last as value:
return Object.fromEntries(entries)
// if you have multiple objects with the same id's, only the last one
// will be in your returned object
}
i want to find if there is atleast one item with completed property with value false from an array of objects using javascript and react.
i have an array of objects like so,
const items = [
{
id: "32",
jobs: [
{
jobs_id: "32",
completed: true,
},
{
jobs_id: "34",
completed: false,
},
],
},
{
id: "31",
jobs: [
{
jobs_id: "30",
completed: true,
},
{
jobs_id: "33",
completed: true,
},
],
},
{
id: "30",
jobs: [
{
jobs_id: "31",
completed: true,
}
],
}
]
selected_items = {
32: 1,
31: 1,
}
Now i want to check if the items has atleast one object with job completed false for the id 32 and 31 .
basically i have to filter objects with selected_items and then check if the job object completed property value is false.
In the above example selected_items has 32 and 31 ids. so i have to filter items from items array of object with ids 32 and 31 and see if either of their jobs object has completed: false.
in above example we see that item with id "32" has job with job_id "34" has completed: false.
So i am expecting the output to be true if atleast one of the items with ids 32 or 31 has jobs with completed: false. otherwise false.
how can i check this using javascript.
I am new to programming and learning javascript. could someone help me with this thanks.
EDIT:
what i have tried?
i know that filtering is possible using filter and array.includes() method.
so if i had selected_items = [32,31] then i could do something like below to get items with id 32 and 31
const res = items.filter((item) => selected_items.includes(item.id);
but in my case selected_items is object. and this just filters the items for given ids. i want to still check if the items filtered based on ids have atleast on completed: false.
const items = [
{
id: "32",
jobs: [
{
id: "32",
completed: true,
},
{
id: "34",
completed: false,
},
],
},
{
id: "31",
jobs: [
{
id: "30",
completed: true,
},
{
id: "33",
completed: true,
},
],
},
{
id: "30",
jobs: [
{
id: "31",
completed: true,
}
],
}
]
const selected_items = { 32: 1, 31: 1, }
const filteredItems = items.filter(item => Object.keys(selected_items).includes(item.id))
console.log(filteredItems);
const extractCompletedValues = filteredItems.map(item => item.jobs.map(job => job.completed))
console.log(extractCompletedValues)
// returns the array from [[true, false]] to [ true, false]
const booleanList = extractCompletedValues.flat();
console.log(booleanList)
// every will return true if all the values in the Array are true else false
const hasOneNonCompletedItem = booleanList.every(Boolean)
console.log(hasOneNonCompletedItem); // will return false if there is atleast one false
Try this:
var selected_items = { 32: 1, 31: 1, }
var arrOb =Object.keys(selected_items );
var res = items.filter((item) =>arrOb.includes(item.id));
var flag = false ;
for(let i = 0 ;i<res.length;i++){
if(res[i].jobs.some(ele=>ele.completed===true)){
flag = true ;
break ;
}
}
if(flag){console.log("it contains completed true")}
else{console.log(false)}
I'm just starting with rethinkDB query lang and don't understand how to select the first key (name).
In this case I would be the object notes.
I did it with Object.keys(t)[0]; tries that just returns me args, what am I doing wrong?
{
id: "mission#0",
info:
"Tokyo",
**//how to get the value of content ?**
note: {
"note#032b8836-f647-4165-9ec9-fc22769f3ffa": {
content: "hello world",
context: "all",
glyphId: "glyph#default",
id: "note#032b8836-f647-4165-9ec9-fc22769f3ffa",
meta: {
context: null,
createdAt: 1624044683953,
id: "note#032b8836-f647-4165-9ec9-fc22769f3ffa",
links: null,
parentEntity: "mission#0b1cd61d-bb36-4cf8-8f3d-9cc9b14ff054",
references: { glyphId: "glyph" },
rootAggregateId: "mission#0b1cd61d-bb36-4cf8-8f3d-9cc9b14ff054",
rootAggregatePath: [
"private",
"notes",
"note#032b8836-f647-4165-9ec9-fc22769f3ffa",
],
status: "published",
summaries: {
description: "hello world",
glyph: "bookmark",
glyphColor: "base",
info: "hello world",
},
type: "note",
values: null,
version: 0,
},
},
}
function* extract(next) {
const q = r
.db("test")
.table("mission")
.getAll(true, { index: "recurrenceEnabled" })
.filter((mission) => mission("meta")("status").eq("published"))
.map((m) => {
let t = m("private")("notes");
let p = Object.keys(t)[0];
return {
note: t,
id: m("id"),
info: m("meta")("summaries")("info"),
};
});
return yield q.run(con, next);
}
Thank you for the reading !
I have an object in my Vue instance with the name of items
<script>
export default {
data() {
return {
selected: "",
items: {
item1: [{ selected: "", inputType: "", inputTarget: "" }],
item2: [{ selected: "", inputType: "", inputTarget: "" }]
},
textarea: ""
};
},
methods: {
selectboxAction(index) {
this.items.item1.forEach(val => {
if (val.selected.toLowerCase() === "file") {
this.$refs.inputData[index].type = "file";
} else {
this.$refs.inputData[index].type = "text";
}
});
}
}
};
</script>
how can I fetch an array of it, I want to put some condition over every item, but it could have item more than 2 items, maybe in the future it reaches 100
as you can see in selectboxAction method, I was able to fetch only one item which is item1 in this case
how can I fetch all of the arrays from items, not just one item1
I suggest you format your data using a computed getter and use Object.keys as others have suggested.
get itemsArray() {
return Object.keys(this.items).map((key) =>
return this.items[key];
});
}
Instead of naming your 'items' properties 'item1', 'item2', etc, it would be better make 'items' an array and add an 'id' property to each 'items' object:
data() {
return {
selected: "",
items: [
{ id: 1, selected: "", inputType: "", inputTarget: "" },
{ id: 2, selected: "", inputType: "", inputTarget: "" }
],
textarea: ""
};
},
you can do something like
methods: {
selectboxAction(index) {
Object.keys(this.items).forEach(val => {
this.items[val].forEach(item => {
if (item.selected.toLowerCase() === "file") {
this.$refs.inputData[index].type = "file";
} else {
this.$refs.inputData[index].type = "text";
}
});
});
}
}
I have an object router shown below. lets say I have another var x = "/podcast" and I want to find which key this variable x would reside. For example in this case I want to return the value "M2"
How can I search this object router and it's sub-objects to return the appropriate key?
Thanks!
var router = {
M1: {
url: "/",
description: "",
title: "",
image: "",
},
M2: {
url: "/podcast",
description: "",
title: "",
image: "",
},
M3: {
url: "/about",
description: "",
title: "",
image: "",
},
};
You can use Object.keys() to get Array of object keys then .filter(), here is a working snippet:
const search = '/podcast';
const router = {
M1: {
url: "/",
description: "",
title: "",
image: ""
},
M2: {
url: "/podcast",
description: "",
title: "",
image: ""
},
M3: {
url: "/about",
description: "",
title: "",
image: ""
}
};
const foundElement = Object.keys(router).filter((el) => router[el].url === search);
console.log(foundElement[0]);
To get your keys you use Object.keys(object)
To search the key you can use filter() or find().
To apply the condition you define callback function inside filteror find.
In my case, I used find function :
const foundElement = (router, searchableValue) => {
const founded = Object.keys(router).find(
(value) => router[value].url === searchableValue
);
return founded === undefined ? "Key not found" : founded;
};
const searchableValue = "/podcast";
console.log(foundElement(router, searchableValue));