Convert string into nested objects and arrays - javascript

I am in a project where the previous guy did some strange routing and basically I get slugs from contentful in the form of strings which I put in an array ["/api/", "/docs/getting-started/", "/docs/, "/plugin/", "/plugin/user/", "/plugin/profile/"......]
Now I need to convert it in the form of an array
let cleanedSidebarContents = [{
title:null,
pages:[
{title:"api",
path:"/api/"
},
{title:"docs",
path:"/docs/"
},
{title:"plugin",
path:"/plugin/"
},
]
},
{
title:"plugin",
pages:[
{title:"user",
path:"/plugin/user/"
},
{title:"profile",
path:"/plugin/profile/"
},
]
},
{
title:"docs",
pages:[
{title:"getting-started",
path:"/plugin/getting-started/"
},
]
}
]
So currently what I am doing is this -
//-------Format sidebar data--------------->
let cleanedSidebarContents = [];
(function cleanSidebarContents() {
let sideBarContentsContentful = [];
cleanContentfulEdges.forEach(edge => {
let slug = edge.node.slug;
//split string into titles and remove empty spaces
let routeMapArray = slug.split("/").filter(x => x != "");
if (routeMapArray.length > 1) {
sideBarContentsContentful.push({
title: routeMapArray[0],
page: {
title: routeMapArray[routeMapArray.length - 1],
path: edge.node.slug
}
});
} else {
sideBarContentsContentful.push({
title: null,
page: {
title: routeMapArray[routeMapArray.length - 1],
path: edge.node.slug
}
});
}
});
let titles = [];
for (let i = 0; i < sideBarContentsContentful.length; i++) {
titles.push(sideBarContentsContentful[i].title);
}
//clean duplicate entries
titles = titles.filter(function (item, index, inputArray) {
return inputArray.indexOf(item) === index;
});
titles.sort();
titles.map(item => {
cleanedSidebarContents.push({
title: item,
pages: []
})
});
sideBarContentsContentful.forEach(item => {
for (let i = 0; i < cleanedSidebarContents.length; i++) {
if(cleanedSidebarContents[i].title === item.title){
cleanedSidebarContents[i].pages.push(item.page)
}
}
});
}());
//----------------------------------------->
I am first splitting all strings and putting the titles in a titles array then removing duplicates and mapping data accordingly.
I just feel like this is really bad code and there is a better way I just cannot figure out.

You could create a mapper object which maps the objects in the output with the title in the root of each object. split at the / to get all the chunks of path. .filter(Boolean) removes all the empty string created from beginning and end of the string. If there is only one match, then it belongs to the default null object. Else, use the first key in the matches as a key in the accumulator.
const input = ["/api/", "/docs/getting-started/", "/docs/", "/plugin/", "/plugin/user/", "/plugin/profile/"];
const mapper = input.reduce((acc, path) => {
let matches = path.split(/\//).filter(Boolean),
mapperKey = null;
if (matches.length > 1)
mapperKey = matches[0];
if (!acc[mapperKey])
acc[mapperKey] = { title: mapperKey, pages: [] };
const title = matches.pop();
acc[mapperKey].pages.push({ title, path });
return acc;
}, {})
const output = Object.values(mapper);
console.log(output)

Related

React-Native array object filtering not working as expected

I'm working on the react-native project and I have an array object which is coming from backend.
here is the sample of an Array.
{
"2010":[
{
"id":1243,
"eventName":"sample_01",
"categoryType":"CUSTOM_NOTES",
"tags":"tag19",
"lastModified":"2022-10-04T14:31:32Z",
"attachments":[
]
}
],
"2022":[
{
"id":1244,
"eventName":"sample_02",
"categoryType":"CUSTOM_NOTES",
"tags":"tag1, tag12, tag3, tag52, tag19",
"lastModified":"2022-10-04T14:31:32Z",
"attachments":[
]
},
{
"id":1245,
"eventName":"hello_03",
"categoryType":"CUSTOM_NOTES",
"tags":"tag1, tag12",
"lastModified":"2022-10-04T14:31:32Z",
"attachments":[
]
}
]
}
In this array, it has some tags which were provided by the user previously (ex: "tags":"tag1, tag12").
There can be multiple or single tags in the array object. Using those tags or tags user should be able to filter the items in the array.
But my code is not working for the above behavior and it filters the elements with a tag, which is selected at last by the user.
Please help me to find an answer. Thanks in advance.
My code for the filtering as follows.
FilterByTagsOrAttachements = (advancedFilterData) => {
let advancedFilterFn = {};
if (advancedFilterData[0].filterType === "Tag") {
for (let advancedItem of advancedFilterData) {
for (const itemTag of advancedItem.data) {
advancedFilterFn = (a) => {
for (let tag of a.tags.split(',')) {
return isEqual(tag, itemTag);
}
};
}
}
} else if (advancedFilterData[1].filterType === "Attachments") {
console.log(JSON.stringify(advancedFilterData[1].data));
}
this.onCreateAdvancedFilterObject(advancedFilterFn);
}
onCreateAdvancedFilterObject = (advancedFilterFn) => {
this.setState(state => {
const events = {};
for (const [year, items] of Object.entries(state.events)) {
events[year] = items.slice().filter(advancedFilterFn);
}
return { events }
})
}
and advancedFilterData array is as follws.
advancedFilterData => [{"filterType":"Tag","data":["tag1","tag12"]},{"filterType":"Attachments","data":[]}]
I guess you need to store all the filters inside an array and then use this array to check if it includes all the elements. Also, since you have , , you should split with , unless you handle it accordingly inside your isEqual method.
FilterByTagsOrAttachements = (advancedFilterData) => {
let advancedFilterFn = {};
let filterArray = []
if (advancedFilterData[0].filterType === "Tag") {
for (let advancedItem of advancedFilterData) {
filterArray = filterArray.concat(advancedItem.data)
}
advancedFilterFn = (a) => {
for (let tag of a.tags.split(', ')) {
return filterArray.includes(tag);
}
};
} else if (advancedFilterData[1].filterType === "Attachments") {
console.log(JSON.stringify(advancedFilterData[1].data));
}
this.onCreateAdvancedFilterObject(advancedFilterFn);
}

Pushing element into array inside for loop

I want to dynamically generate a 50 element array from a single element, by just modifying one value on each loop.
const eventRecords = { Records: [] }
for (let i = 0; i < 50; i++) {
const aRecord = Object.assign({}, eventS3Chunk.Records[0])
aRecord.s3.object.key = `F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/${i}.csv`
eventRecords.Records.push(Object.assign({}, aRecord))
}
eventRecords.Records end up with 50 copies of same element with s3.object.key = F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/49.csv.
it's because you're creating a shallow copy of Records[0], use JSON.parse(JSON.stringify(eventS3Chunk.Records[0]));
:
const eventS3Chunk = {
Records: [{
s3: {
object: {
key: "a"
}
}
}]
};
const eventRecords = Array.from({
length: 50
}, (_, i) => {
const aRecord = JSON.parse(JSON.stringify(eventS3Chunk.Records[0]));
aRecord.s3.object.key = `F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/${i}.csv`;
return aRecord;
});
console.log(eventRecords)

How to get text by id from array? React

CurrentUserAnswerVariants:
{id: "4468cdc8-220d-4634-9d68-6c9920e0cb48", text: "Question 1",
minAnswersQuantity: 1, maxAnswersQuantity: 1, canComment: false,
canComment: false, currentUserAnswerVariants: ["ecc0b93c-8e3b-4661-8f2e-f5382a74d79b"], id: "4468cdc8-220d-4634-9d68-6c9920e0cb48" }
const answers = [];
this.setState({questionGroups}, () => {
this.state.questionGroups.map((questionGroup) => {
questionGroup.questions.map((question) => {
// questionGroup.questions.variants.map((variant) => {
const currentUserAnswerVariantsVariantIds = question.currentUserAnswerVariants.filter(variant => ["canComment"].indexOf(variant) === -1);
const currentUserAnswerVariantsVariantText = question.currentUserAnswerVariants.filter(variant => currentUserAnswerVariantsVariantIds === variant.id).text;
const answer = {
questionId: question.id
};
if (!isNull(question.currentUserAnswerComment)) {
answer["comment"] = question.currentUserAnswerComment;
}
if (currentUserAnswerVariantsVariantIds.length) {
answer["variantIds"] = currentUserAnswerVariantsVariantIds;
}
if (currentUserAnswerVariantsVariantText) {
answer["variantText"] = currentUserAnswerVariantsVariantText;
}
answers.push(answer);
console.log(questionGroup.questions.variants);
// });
});
});
});
};
How to get by currentUserAnswerVariants value text from variants array by id?
My array Please can u see this image where is my array and when i check some variant i have new array Answer array
function idEquals(idToCompare, question) {
return question.id.indexOf(idToCompare) === 0;
}
currentUserAnswerVariantsVariantIds = question.filter(idEquals.bind(this, currentUserAnswerVariants));
Does this work?
It's something along these lines, and you may edit the following code accordingly.
$var = $('#id').val();
$var['text'];
-> only if you need single id text
-> or use foreach on id and push the each result into one blank array and print array

Filter Array that contains specific symbol or words in Javascript

Hello I would like to ask for help on filtering my Array currently I have a list of array that contains words but I want to filter out those with symbol ("#") to be remove on the array
function InitializeV3() {
var req = SymbolList; //obj with symbol property
for (var i = 0; i < req.lenght; i++) {
if (req[i].smybol.includes("#")) {
req.splice(req[i], 1);
}
}
console.log(req);
};
For a simple array you can do it like this with the filter method:
var req = ['test','#test1','#test2','test3','test4'];
var result = req.filter(item => !item.includes("#"));
console.log(result);
And if you have an array of objects:
var req = [{symbol: 'test'},{symbol: '#test1'},{symbol: '#test2'},{symbol: 'test3'},{symbol: 'test4'}]
var result = req.filter(item => !item.symbol.includes('#'));
console.log(result);
function InitializeV3() {
// For simple array
var req = ['test',
'#test1',
'#test2',
'test3',
'test4'
]
var filtered = req.filter(item => !item.includes('#'))
console.log(filtered)
};
InitializeV3();
// For array of objects
var req = [{
symbol: 'test'
}, {
symbol: '#test1'
}, {
symbol: '#test2'
}, {
symbol: 'test3'
}, {
symbol: 'test4'
}]
// Use the following
var filtered = req.filter(item => !item.symbol.includes('#'))
console.log(filtered)
First of all - req array should contain objects (current syntax is incorrect):
var req = [
{ symbol: 'test' },
{ symbol: '#test1' },
// ...
];
Then you can try with:
const filteredReq = req.filter(item => item.symbol.indexOf('#') === -1);
You can loop over all the keys. But if you have symbol multiple times as key only the last data will be saved.
let SymbolList = {
symbol0:'test',
symbol1:'#test1',
symbol2: '#test2',
symbol3:'test3',
symbol4: 'test4'
};
function InitializeQuotesV3(req) {
for (key in req) {
req[key] = req[key].replace(/#/g,"");
}
return req;
};
console.log(InitializeQuotesV3(SymbolList));
Better you can use JS Regex Regex
var req = [{symbol:'test'} ,
{symbol:'#test1'},
{symbol: '#test2'},
{symbol:'test3'} ,
{symbol: 'test4'}];
let hasHash = /#/;
for (var i = req.length - 1; i >= 0; i--) {
if (hasHash.test(req[i].symbol)) {
req.splice(i,1);
}
}
console.log(req);

How to get from an array of objects all unique values of a property that is an array itself

This answer is already close, and there are some answers how to get unique values in an array (remove duplicates,)though I can't make it work for the case where it is about an array of objects, and the property that should be filtered is an array. Sorry, I am a JS newbie. Thanks for the help.
I have an array of objects like this
const posts = [
post1: {
id: 1,
title: 'One',
tags: ['tagA', 'tagB']
},
post2: {
id: 2,
title: 'Two',
tags: ['tagB', 'tagC']
},
post3: {
id: 3,
title: 'Three',
tags: ['tagB', tagC, tagD]
]
What I would need is an array of all unique tags ... in the case above with an expected output like this:
// [tagA, tagB, tagC, tagD]
EDIT / UPDATE
The key in the array of objects is used to manage the state of the react component... e.g.
constructor(props) {
super(props);
this.state = {
posts: []
};
}
...
updatePost = (key, updatedPost) => {
//1. Take copy of the current this.state.
const posts = {...this.state.texts};
//2. Update that state
posts[key] = updatedPost;
//3. Set that to state
const options = { encrypt: false }
putFile(postsFileName, JSON.stringify(posts), options)
.then(() => {
this.setState({
posts: posts
})
})
};
Assuming that the input is on [ {} , {} ] format:
You can use concat and map to flatten your array. Use new Set to get the unique values.
const posts = [{"id":1,"title":"One","tags":["tagA","tagB"]},{"id":2,"title":"Two","tags":["tagB","tagC"]},{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}];
var result = [...new Set([].concat(...posts.map(o => o.tags)))];
console.log(result);
If the variable is an object ( {a:{} , b:{} } ) , you can use Object.values to convert the object into an array.
const posts = {"post1":{"id":1,"title":"One","tags":["tagA","tagB"]},"post2":{"id":2,"title":"Two","tags":["tagB","tagC"]},"post3":{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}}
var result = [...new Set([].concat(...Object.values(posts).map(o => o.tags)))];
console.log(result);
You can reduce your posts and iterate over the tags and push those to the result that you haven't encountered already:
const posts = [
{
id: 1,
title: "One",
tags: ["tagA", "tagB"]
},
{
id: 2,
title: "Two",
tags: ["tagB", "tagC"]
},
{
id: 3,
title: "Three",
tags: ["tagB", "tagC", "tagD"]
}
];
const uniqueTags = posts.reduce((result, post) => {
post.tags.forEach(tag => {
if (!result.includes(tag)) {
result.push(tag);
}
});
return result;
}, []);
console.log(uniqueTags);
This is assuming you know that the array key is always 'tags'.
let filter = {};
let result = [];
posts.forEach(post => {
const tags = post['tags'];
tags.forEach(tag => {
if (!filter.hasOwnProperty(tag)) {
result.push(tag);
filter[tag] = true;
}
});
});
with jquery you can do something similar to this (not Tested):
var results = [];
$.each(myObject, function(key,valueObj){
var check.isArray(obj);
if(check){
alert(key + "/" + valueObj );
/*replace repeat*/
var sorted_check = check.slice().sort(); // You can define the comparing function here.
// JS by default uses a crappy string compare.
// (we use slice to clone the array so the
// original array won't be modified)
for (var i = 0; i < sorted_check.length - 1; i++) {
if (sorted_check[i + 1] == sorted_check[i]) {
results.push(sorted_check[i]);
}
}
}
});
and a good way with indexof:
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
this.length = 0;
for ( i = 0; i < a.length; i++ ) {
this.push( a[i] );
}
return this;
}
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
return a;
}
And Follow UP:
Array.prototype.unique = function(mutate) {
var unique = this.reduce(function(accum, current) {
if (accum.indexOf(current) < 0) {
accum.push(current);
}
return accum;
}, []);
if (mutate) {
this.length = 0;
for (let i = 0; i < unique.length; ++i) {
this.push(unique[i]);
}
return this;
}
return unique;
}
If you want to use a functional library like Ramda.js you can do this:
const posts = [
{
id: 1,
title: 'One',
tags: ['tagA', 'tagB'],
},
{
id: 2,
title: 'Two',
tags: ['tagB', 'tagC'],
},
{
id: 3,
title: 'Three',
tags: ['tagB', 'tagC', 'tagD'],
},
];
var unique = R.uniq(R.flatten(R.map(R.prop('tags'), posts)))
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Categories