I have this piece of code that removes characters such as whitespace from property names:
let result = rows.map(el => {
let resultobj = {};
Object.entries(el).map(([key, val]) => {
resultobj[key.split(" ").join("").replace(/\W+/g, '')] = val;
return resultobj;
} )
return resultobj;
})
There is a property named "AgendaItem", I would like to remove the word "Item" from the name, leaving only "Agenda". How can I add this requirement to the current code, above?
Thank you for your help,
Erasmo
UPDATE - I tored the code below, to replace File # with Legistar, and LegistarID with Legistar
let result = rows.map(el => {
let resultobj = {};
Object.entries(el).map(([key, val]) => {
resultobj[key.split(" ").join("").replace(/\W+|Item$/g, '').replace("File #","Legistar").replace("LegistarID","Legistar")] = val;
return resultobj;
})
return resultobj;
})
console.log(result);
After execution, result contains:
0
:
{File: '75588', Ver: '1', Agenda: '1', BoardsCommissionsandCommittees: 'Public Comment', Type: 'Public Comment', …}
1
:
{File: '75590', Ver: '1', Agenda: '2', BoardsCommissionsandCommittees: 'Lake Update', Type: 'Miscellaneous', …}
2
:
{File: '75592', Ver: '1', Agenda: '3', BoardsCommissionsandCommittees: 'Booking Pace Update:', Type: 'Miscellaneous', …}
3
:
{File: '75594', Ver: '1', Agenda: '4', BoardsCommissionsandCommittees: 'Finance Report: ', Type: 'Miscellaneous', …}
4
:
{File: '75595', Ver: '1', Agenda: '5', BoardsCommissionsandCommittees: 'Director’s Report: ', Type: 'Miscellaneous', …}
5
:
{File: '75596', Ver: '1', Agenda: '6', BoardsCommissionsandCommittees: 'Announcement from the Chair: , Chair', Type: 'Miscellaneous', …}
You can extend the regex with other stuff you want deleted. Also, that split(" ").join("") is not needed as your current regex already matches spaces.
Finally, you could use Object.fromEntries to build the object in a functional way:
let rows = [{ AgendaItem: 1, "test this": 2, "one, two, three": 3 }];
let result = rows.map(el => Object.fromEntries(
Object.entries(el).map(([key, val]) => [key.replace(/\W+|Item$/g, ''), val])
));
console.log(result);
The $ in the regex makes sure Item is only removed when it is the last part of the key. If you want to remove it from anywhere in the key, then drop that $.
Alternatively, you could define a translation object where the key is the from-text and the value the target text:
const translation = {
"AgendaItem": "Agenda",
"File #": "Legistar",
"LegistarID": "Legistar"
};
const objectMapper = obj => Object.fromEntries(
Object.entries(obj).map(([key, val]) => [
translation[key] ?? key.replace(/\W+/g, ''),
val
])
);
// Demo
const rows = [
{ AgendaItem: 1, "test this": 2, "one, two, three": 3 },
{ LegistarID: 9, "///abc///": 20 },
{ "File #": 12 }
];
const result = rows.map(objectMapper);
console.log(result);
You can check if the key contains the word "Item", if it does, you can replace the "Item" with an empty string. This will remove the word "Item" from any property that have it (not only "AgendaItem"). The rest of the code should work fine as you are already overwriting the properties.
if (key.includes("Item")) {
key = key.replace("Item", "");
}
The if statement can also be changed to key.endsWith("Item"), it would work for your "AgendaItem" scenario.
I think that should do it.
Related
I'm trying to build a function that will extract the values from an object and creates an array of strings.
In the snippet below, you can see an example of what I did until now but that is not fully correct to my requirements.
Let's say I have data as in the example
const data = {
age: {
min: '17',
max: '66'
},
gender: ['male', 'female'],
rosacea: 'true',
rosacea_papulo_pustulosa: 'true',
severe_rosacea: 'true',
nurse_contact: 'true',
};
Right now I'm getting an array of strings for every single value in fact the result if you run it is
[
"17",
"66",
"male",
"female",
"true",
"true",
"true",
"true"
]
But what I need is the following array based if we have or not a nested object inside the data
[
* This is the result of age min: '17' and max: '66'
"17 - 66",
* This is the result of gender male and female
"male - female",
* The rest is ok as no nested objects
"true",
"true",
"true",
"true"
]
The above result is what I'm searching
Another example as this below data
{disease":{"valid":["MDD","PTSD"],"invalid":["None"]}}
// result expected
[
"MDD - PTSD",
"None"
]
My issue at the moment is how to get into the expected results plus needs to be a - between the aggregated results.
We can also have a situation where data looks like this
{ageGroup: ["1","2","3", ..., "n"]}
// result expected
[
"1 - 2 - 3 ... etc ..."
]
The snippets abut my first attempt
const data = {
age: {
min: '17',
max: '66'
},
gender: ['male', 'female'],
rosacea: 'true',
rosacea_papulo_pustulosa: 'true',
severe_rosacea: 'true',
nurse_contact: 'true',
};
const getValues = (data, values = []) => {
if (typeof data !== 'object') {
return [...values, data];
}
return Object.values(data).flatMap((v) => getValues(v, values));
};
console.log(getValues(data))
Update
The nested objects will never go more deeply as per following example
age: {
group1: {
min: '1',
max: '6'
}
group2: {
min: '7',
max: '10'
}
},
Expected result as
[
'1 - 6',
'7 - 10'
]
The OP already came up with a recursive approach.
In order to achieve the OP's expected result(s), one has to implement the recursively operating function in a way that it will concatenate any array-type's item and any of an object-type's value, regardless of the (currently processed) data-structure's nesting level.
And for the not to be concatenated first level entries of the OP's data object one just needs to map the very object's values by said recursive function.
function stringifyDataRecursively(data) {
let result = '';
if (data && (typeof data === 'object')) {
if (Array.isArray(data)) {
result = data
.map(stringifyDataRecursively)
.join(' - ');
} else {
result = Object
.values(data)
.map(stringifyDataRecursively)
.join(' - ');
}
} else {
result = data;
}
return result;
}
const sampleData = {
age: {
min: '17',
max: '66'
},
gender: ['male', 'female'],
rosacea: 'true',
rosacea_papulo_pustulosa: 'true',
severe_rosacea: 'true',
nurse_contact: 'true',
};
console.log(
'stringified `sampleData` ...',
Object
.values(sampleData)
.map(stringifyDataRecursively)
);
const sampleData_2 = {
disease: {
valid: ['MDD', 'PTSD'],
invalid: ['None'],
},
};
console.log(
'stringified `sampleData_2` ...',
Object
.values(sampleData_2)
.map(stringifyDataRecursively)
);
console.log(
'stringified `sampleData_2.disease` ...',
Object
.values(sampleData_2.disease)
.map(stringifyDataRecursively)
);
const sampleData_3 = {
group1: {
min: '1',
max: '6',
},
group2: {
min: '7',
max: '10',
},
};
console.log(
'stringified `sampleData_3` ...',
Object
.values(sampleData_3)
.map(stringifyDataRecursively)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I have an array of objects, and according to a property inserted in one of them i would like to mark or select all the objects previous to that object container of the specific property
My array is in this way:
const arrX= [
{ status: '1' },
{ status: '2'},
{ status: '3', imHere: true },
{ status: '4' },
];
Then due to the property imHere on arrX[2], the positions arrX[0] and arrX[1] should be modified.
My expected result would be :
const arrX= [
{ status: '1',wasHere:true },
{ status: '2',wasHere:true},
{ status: '3', imHere: true },
{ status: '4' },
];
I know that the map method would be quite useful in this case, but can´t find the way to check from index of object containing imHere backwards the former positions
One approach is to use .findIndex() and .map():
const arrX= [{ status: '1' }, { status: '2'}, { status: '3', imHere: true }, { status: '4'}];
const imHereIndex = arrX.findIndex(({imHere}) => imHere === true);
const result = arrX.map((val, index) => index < imHereIndex
? { ...val, wasHere: true }
: val
);
console.log(result);
Even if #Kinglish answer works like a charm I want to share another way to achieve your goal. This road is surely longer than Kinglish ones, never then less is a good alternative.
{ status: '4' },
];
function findProperty(arr) {
const hasProperty = arr.findIndex(el => Object.keys(el).includes('imHere'))
const addNewProperty = arr.map((el,i) => (i < hasProperty) ? {...el, wasHere: true} : el)
return addNewProperty
}
const updatedArray = findProperty(arrX)
console.log(updatedArray)
Here's one method for it using Array#reduce and a boolean to track whether we've encountered inHere
const arrX = [
{status: '1'},
{status: '2'},
{status: '3',imHere: true},
{status: '4'},
];
let found = false,
updated = arrX.reduce((b, a) => {
found = found || (a.hasOwnProperty('imHere') && a.imHere === true)
if (!found) a.wasHere = true;
return b.concat(a);
}, [])
console.log(updated)
A simple loop - breaking out of it when one of the objects contains imHere, otherwise adding in a wasHere property.
function update(arr) {
for (let i = 0; i < arr.length; i++) {
if (!arr[i].imHere) {
arr[i].wasHere = true;
} else {
break;
}
}
return arr;
}
const arr = [
{ status: '1' },
{ status: '2' },
{ status: '3', imHere: true },
{ status: '4' },
];
console.log(update(arr));
how to assign the object in object and filter the value which pass and fail;
the input is:
[
{
name: 'John',
score: 90,
time: 'evening'
},
{
name: 'Doni',
score: 68,
time: 'morning'
},
{
name: 'Jiu',
score: 50,
time: 'evening'
},
{
name: 'Shin',
score: 92,
time: 'morning'
},
];
and i want the output like this :
{
"evening": {
"pass": [
{
"name": "John",
"score": 90
}
],
"fail": [
{
"name": "jiu",
"score": 50
}
]
},
"morning": {
"pass": [
{
"name": "Shin",
"score": 92
}
],
"fail": [
{
"name": "Doni",
"score": 68
}
]
}
}
do we need to use Object.assign for this ? and how many loop we use for this ??
i do love to know how to add another string in the object beside that ouput,
thanks
There's a lot of ways to do this. The simplest is probably to make a base object that represent your empty results. Then loop over the students and fill the arrays:
let students = [{name: 'John',score: 90,time: 'evening'},{name: 'Doni',score: 68,time: 'morning'},{name: 'Jiu',score: 50,time: 'evening'},{name: 'Shin',score: 92,time: 'morning'},];
// Empty case
let base = {
"evening": {"pass": [], "fail": []},
"morning": {"pass": [], "fail": []}
}
const PASSING = 70
students.forEach(({name, score, time}) => {
let key = score >= PASSING ? 'pass' : 'fail'
base[time][key].push({name, score})
})
console.log(base)
This makes is easy to have empty arrays, which is probably what you want if there are no students in a particular category.
EDIT based on comment:
To support arbitrary times, you can just create the times on the object as you find them. reduce() is good for this, but you could also use a regular loop. For example with an added afternoon time:
let students = [{name: 'Mark',score: 95,time: 'afternoon'}, {name: 'John',score: 90,time: 'evening'},{name: 'Doni',score: 68,time: 'morning'},{name: 'Jiu',score: 50,time: 'evening'},{name: 'Shin',score: 92,time: 'morning'},];
const PASSING = 70
let result = students.reduce((obj, {name, score, time}) => {
if (!obj[time]) obj[time] = {'pass': [], 'fail': [] }
let key = score >= PASSING ? 'pass' : 'fail'
obj[time][key].push({name, score})
return obj
}, {})
console.log(result)
You can do something like this:
const data = [{ name: 'John', score: 90, time: 'evening' }, { name: 'Doni', score: 68, time: 'morning' }, { name: 'Jiu', score: 50, time: 'evening' }, { name: 'Shin', score: 92, time: 'morning' }, ];
const grp = (d, p) => d.reduce((r,c) => (r[c[p]] = [...r[c[p]] || [], c], r), {})
const grpV = (d, rng) => d.reduce((r,{name, score}) => {
let key = score > rng ? 'pass' : 'fail'
r[key] = [...r[key] || [], {name, score}]
return r
}, {})
const r = Object.entries(grp(data, 'time')).map(([k,v]) => ({[k]: grpV(v, 75)}))
console.log(r)
The idea is the group 2 times one on the time and 2nd on the score.
grp: function to group by a property (in this case 'time') which returns an object with 2 properties: evening and morning each of which is an array containing the classes.
grpV: function to group by value (in this case 75) which returns an object with 2 properties: pass and fail each of which is an array containing the classes.
On the end once we have those tools we are saying ... give me the entries of the grouped by time object and for each of the groups ... group by score.
Here how something like this could look like if we ware using lodash:
const data = [{ name: 'John', score: 90, time: 'evening' }, { name: 'Doni', score: 68, time: 'morning' }, { name: 'Jiu', score: 50, time: 'evening' }, { name: 'Shin', score: 92, time: 'morning' }, ];
const partition = (x, p) => _(x)
.partition(y => y.score > p)
.map((x,i) => ({ [i==0 ? 'pass': 'fail']: _.omit(x[0], 'time')}))
.value()
const r = _(data)
.groupBy('time')
.mapValues(x => partition(x, 75))
.value()
console.log(r)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Adding it as an example since it does help with readability of what the ES6 example is doing to some extend.
I'm sure there are more elegant ways to do this. But this one is probably one of the simplest beginner-friendly ways you can go about this.
I loop through the input array, check the existence of the .time values as keys on the output object and create the pass and fail keys. Then evaluate the .score against the passingScore and push the necessary data to it.
Should be pretty easy to understand once you see and try the code below:
const data = [
{name: 'John',score: 90, time: 'evening'},
{name: 'Doni',score: 68, time: 'morning'},
{name: 'Jiu',score: 50, time: 'evening'},
{name: 'Shin',score: 92, time: 'morning'},
{name: 'Fubar',score: 75, time: 'noon'},
];
function formatData(data){
const passingScore = 75;
const output = {};
data.forEach(function(item){
if(!output[item.time]) output[item.time] = {pass: [], fail: []};
const stud = { name: item.name, score: item.score };
if(item.score >= passingScore) output[item.time]['pass'].push(stud)
else output[item.time]['fail'].push(stud)
});
return output;
}
console.log(formatData(data));
I have a bidimensional array like this:
const bArray =
[ [ 'Hello World',
'Hi everybody',
'How are you?'
],
[ { text: 'Hola Mundo',
from: [Object],
raw: '' },
{ text: 'Hola a todos',
from: [Object],
raw: '' },
{ text: 'Cómo estás?',
from: [Object],
raw: '' },
]
]
And I need to get as a result, only one array that should look like this:
[
{ en: 'Hello World',
es: 'Hola Mundo' },
{ en: 'Hi everybody',
es: 'Hola a todos' },
{ en: 'How are you?',
es: 'Cómo estás?' },
]
This is how I do it:
let val1 = bArray[0].map(tuple => tuple);
let val2 = bArray[1].map(tuple => tuple);
let result = val1.reduce((arr, v, i) => arr.concat({"en" : v, "es" : val2[i].text}), []);
And now in the result variable, I have only one array with the result showed before.
My question?
Is there any improved way in which I can get the same result but with fewer lines of code? I mean, something like a combination of map with reduce, filter or concat without creating two separte arrays like val1 and val2.
If you simply do:
bArray[0].reduce((arr, v, i) => arr.concat({"en" : v, "es" : bArray[1][i].text}), []);
You can get the same thing in just one line.
Explanation:
let val1 = bArray[0].map(tuple => tuple);
let val2 = bArray[1].map(tuple => tuple);
This is doing nothing but get the elements in the array. It's exactly the same thing as:
let val1 = bArray[0];
let val2 = bArray[1];
So I just direct accessed the bArray indexes in your original line.
I am assuming there will be only two arrays inside outer array. You simply need to loop on first array and pick the text from from other array at same index and merge them into one object.
const bArray =
[ [ 'Hello World',
'Hi everybody',
'How are you?'
],
[ { text: 'Hola Mundo',
from: [],
raw: '' },
{ text: 'Hola a todos',
from: [],
raw: '' },
{ text: 'Cómo estás?',
from: [],
raw: '' },
]
];
let result = bArray[0].map((item, index) => {
return {
en: item,
es: bArray[1][index].text
};
});
console.log(result);
Yet another variation using Array.prototype.forEach() method
let result = [];
bArray[0].forEach((item, index) => {
result.push({
en: item,
es: bArray[1][index].text
});
});
console.log(result);
I have an object that I receive from html form, and it has the following syntax:
'ingredients[0][name]': 'eggplant',
'ingredients[0][mass]': '1',
'ingredients[0][units]': 'pc',
'ingredients[1][name]': 'cheese',
'ingredients[1][mass]': '150',
'ingredients[1][units]': 'gr',
'ingredients[2][name]': 'cilantro',
'ingredients[2][mass]': '100',
'ingredients[2][units]': 'tt' ...
All that I need is to convert these key-value pairs into the one big object field. Such as
recipe = {
ingredients: [
{
name: 'epplant',
mass: '1',
units: 'pc'
},
{
name: 'cheese',
mass: '150',
units: 'gr'
}, ...
]
}
How can I do this without JQuery or other JS-framework?
var form = {
'ingredients[0][name]': 'eggplant',
'ingredients[0][mass]': '1',
'ingredients[0][units]': 'pc',
'ingredients[1][name]': 'cheese',
'ingredients[1][mass]': '150',
'ingredients[1][units]': 'gr',
'ingredients[2][name]': 'cilantro',
'ingredients[2][mass]': '100',
'ingredients[2][units]': 'tt'
}
var re = /([a-zA-Z][a-zA-Z_0-9]*)\[(\d+)\]\[([a-zA-Z][a-zA-Z0-9_]*)\]/
var result = {}
for (var key in form) {
var match = key.match(re)
if (!match) continue
var arr = result[match[1]]
if (!arr) arr = result[match[1]] = []
var obj = arr[match[2]]
if (!obj) obj = arr[match[2]] = {}
obj[match[3]] = form[key]
}
console.log(result)
http://jsfiddle.net/r1gkog1b/
UPD: some explanation:
I think, you should iterate throw your input form object keys and parse its with regexp. In case of match you can construct desirable output structure