Here's a snippet of my JSON object (EDITED) -
[{
"traditional": "%",
"simplified": "%",
"referencedTraditional": [],
"referencedSimplified": [],
"pinyinNumeric": "pa1",
"pinyinDiacritic": "pā",
"definitions": [
"percent (Tw)"
],
"definitionsDiacritic": [
"percent (Tw)"
]
},
{
"traditional":"龠","simplified":"龠","referencedTraditional":[{"cp":"9fa0","c":"龠"}],"referencedSimplified":[{"cp":"9fa0","c":"龠"}],"pinyinNumeric":"yue4","pinyinDiacritic":"yuè","definitions":["ancient unit of volume (half a 合[ge3], equivalent to 50ml)","ancient flute"],"definitionsDiacritic":["ancient unit of volume (half a 合[gě], equivalent to 50ml)","ancient flute"]},
{"traditional":"龡","simplified":"龡","referencedTraditional":[{"cp":"9fa1","c":"龡"}],"referencedSimplified":[{"cp":"9fa1","c":"龡"}],"pinyinNumeric":"chui4","pinyinDiacritic":"chuì","definitions":["to blow (a flute)","archaic version of 吹"],"definitionsDiacritic":["to blow (a flute)","archaic version of 吹"]},
]
I want to convert every key called definitions from an array into a string.
E.g. From ["percent (Tw)"] to "percent (Tw)". Basically, I don't want the array brackets.
So far, I've tried looping through the file and converting every "definitions" key with JSON.stringify() or toString() -
translations.forEach(key => JSON.stringify((key.definitions)))
However, nothing changes in the outputted file.
Issue with the code is the one below
translations.forEach(key => JSON.stringify((key.definitions)))
Simply running this statement will not update the value for the key inside that object. You have to update the object for this.
Logic.
Loop through the keys in the object translations. I used Object.entries(translations).forEach for that.
Check each key, whether it includes "definitions" in that key.
If the key has "definitions" in that, run your stringification logic. I prefer Array.join() compared to JSON.stringify.
Update the key of that object.
Working Fiddle
const translations = {
"traditional": "%",
"simplified": "%",
"referencedTraditional": [],
"referencedSimplified": [],
"pinyinNumeric": "pa1",
"pinyinDiacritic": "pā",
"definitions": [
"percent (Tw)"
],
"definitionsDiacritic": [
"percent (Tw)"
]
}
Object.entries(translations).forEach(([key, value]) => key.includes('definitions') ? translations[key] = translations[key].join("") : null);
console.log(translations);
const obj = {
traditional: '%',
simplified: '%',
referencedTraditional: [],
referencedSimplified: [],
pinyinNumeric: 'pa1',
pinyinDiacritic: 'pā',
definitions: ['percent (Tw)'],
definitionsDiacritic: ['percent (Tw)'],
};
const returnStringDefinitions = (obj) => {
const arr = [];
for (const [key, value] of Object.entries(obj)) {
if (key === 'definitions') arr.push(value.toString());
}
return arr;
};
console.log(returnStringDefinitions(obj));
I think what may be happening for you is that you aren't saving the resulting array anywhere. The approach could be to create a new object.
const obj = {
traditional: '%',
simplified: '%',
referencedTraditional: [],
referencedSimplified: [],
pinyinNumeric: 'pa1',
pinyinDiacritic: 'pā',
definitions: ['percent (Tw)'],
definitionsDiacritic: ['percent (Tw)'],
};
const output = Object.keys(obj).reduce((accumulator, currentKey) => {
const currentValue = obj[currentKey];
if (currentKey === 'definitions' && Array.isArray(currentValue)) {
accumulator[currentKey] = currentValue.join('');
} else {
accumulator[currentKey] = currentValue;
}
return accumulator;
}, {})
console.log(output);
This solution checks if it is an array, and also just joins everything in the array just in case there are multiple things in the array, but how this is handled is really up to you :)
Related
I have an array arr1 and an object arr2 with a nested config array.
If the object of arr1 has a key that matches with an id in that nested config and deeper questions array, then change that key (in the arr1 object) with the title property that is found next to that id.
Here is an example. The key isWorking from arr1 is the same as arr2.config[0].questions[0].id value, so
change that isWorking key to the value found in arr2.config[0].questions[0].custom.title.
var arr1= [
{"jobs": "Marketing","isWorking": yes,"country": "MY"},
{"country": "IN","members": 4}
]
var arr2=
{
"id":1,
"name":"xxx",
"config":[
{
"questions":[
{
"id":"isWorking",
"custom":{
"title":"Are you working?"
}
},
{
"id":"jobs",
"custom":{
"title":"Please specify job(s)"
}
}
]
},
{
"questions":[
{
"id":"country",
"custom":{
"title":"which Country?"
}
},
{
"id":"members",
"type":"choices",
"custom":{
"title":"How many members?"
}
}
]
}
]
}
Expected output:
[
{"Please specify job(s)": "Marketing","Are you working": yes,"which Country": "MY"},
{"which Country": "IN","How many members": 4}
]
I tried:
var result = arr1.map(e => ({
arr2.config.find(i => {
i.questions.find( q => {
q.id === Object.key(e) ? Object.key(e) === q.custom.title : q.id
}
})
}))
In your code the find callbacks do not return anything. When you have a code block (with braces) you need a return statement. Also, the outer object literal which you have as the return value of the map callback cannot have just the find call in it. It should have the proper object literal syntax, like with spread syntax. Moreover, the find method can only return an existing object, not a new, modified one.
I will assume here that the matching strings for the first object have to be found in the first questions array, and for the second object in the second questions array.
I also propose to rename arr2, because it isn't an array. It is a plain object, with a property that is an array (config).
Here is how you could do it with Object.fromEntries and Object.entries:
const arr1 = [{"jobs": "Marketing","isWorking": "yes","country": "MY"}, {"country": "IN","members": 4}];
const obj = {"id":1,"name":"xxx","config":[{"questions":[{"id":"isWorking","custom":{"title":"Are you working?"}},{"id":"jobs","custom":{"title":"Please specify job(s)"}}]},{"questions":[{"id":"country","custom":{"title":"which Country?"}},{"id":"members","type":"choices","custom":{"title":"How many members?"}}]}]}
const result = arr1.map((e, i) => Object.fromEntries(
Object.entries(e).map(([key, value]) =>
[obj.config[i].questions.find(({id}) =>
key === id
)?.custom?.title ?? key, value]
)
))
console.log(result);
I have the following array which I have extracted from csv file
array
[
{ "Date;Department;Value": "2022-09-08;dept1;7856"},
{ "Date;Department;Value": "2022-09-08;dept2;9876"}
]
I need the following output:
[
{
"Date":"2022-09-08",
"Department":"dept1",
"Value":7856
},
{
"Date":"2022-09-08",
"Department":"dept2",
"Value":9876
}
]
That's quite doable, just take the problem step by step:
const array = [
{ "Date;Department;Value": "2022-09-08;dept1;7856" },
{ "Date;Department;Value": "2022-09-08;dept2;9876" }
];
const result = array
.map(Object.entries) // Convert the objects to more easily accessible arrays.
.map(([[k, v]]) => { // Grab the key / value strings
const keys = k.split(';'); // Split the keys into separate entries.
const values = v.split(';'); // Split the values into separate entries.
// Join the array of keys with the array of values, parsing numbers if possible.
return keys.reduce((obj, key, i) => {
obj[key] = isNaN(values[i]) ? values[i] : Number(values[i]);
return obj;
}, {});
});
console.log(result);
There is an initial array that needs to be mapped and filtered at the same time:
let data = [
{
"id": 1,
"parentId": null,
"dxId": "1_id",
"dxParentId": null,
"defName": "group1"
},
{
"id": 12,
"parentId": null,
"dxId": "12_id",
"dxParentId": null,
"defName": "группа3"
},
{
"id": 8,
"parentId": 1,
"dxId": "1_id/13",
"dxParentId": "1_id",
"defName": "group4"
},
{
"id": 5,
"parentId": 1,
"dxId": "1_id/20",
"dxParentId": "1_id",
"defName": "user1"
},
{
"id": 5,
"parentId": 1,
"dxId": "1_id/20",
"dxParentId": "12_id",
"defName": "user1"
},
];
I filter by the presence of the parentide property, and collect the resulting array of strings (not the initial array of objects).
originally I did it through reduce methode:
resultArr = data.reduce((filtered, obj) => {
if( obj.dx_parentId !== null) {
if(!(filtered.includes(obj.dxParentId))) {
filtered.push(obj.dxParentId);
}
}
return filtered;
}, []);
console.log(resultArr , 'result'); // ['1_id', '12_id'] 'result'
then I discovered for myself that this can be done with the flatMap array method
resultArr = data.flatMap((obj) => obj.dxParentId !== null ? [obj.dxParentId] : []);
console.log(resultArr , 'result'); //['1_id', '1_id', '12_id'] 'result'
If you notice that in the case of reduce I use filtered (accumulating array) I do an additional check using includes and I get the result that I need.
The question is how to do the same it via flatMap? is it possible to access the accumulating result at each iteration?
Additionally: can someone tell me what is better to use to solve this case, in terms of optimization? mb forEach?
thanks in advance
You cannot access the array that is being built in flatMap. In mapping, and also mapping-with-flattening, the callback is supposed to return a value that depends only on the particular item.
You might simple not want to use reduce at all for an imperative algorithm like this:
const resultArr = [];
for (const obj of data) {
if (obj.dxParentId !== null) {
if (!resultArr.includes(obj.dxParentId)) {
filtered.push(obj.dxParentId);
}
}
}
console.log(resultArr, 'result');
However, to get unique values from an array there is a much better (simpler and more efficient) way:
const resultArr = Array.from(new Set(data.map(obj => obj.dxParentId).filter(id => id !== null)));
console.log(resultArr, 'result');
So while you can't view the array flatmap is building while its building it, you can create an object just outside of flatmap and use it to track which ids you've added or not.
const seen = {};
const resultArr = data.flatMap((obj) => {
const id = obj.dxParentId;
if (id in seen) return [];
if (id !== null) {
seen[id] = true;
return [id];
}
return [];
});
console.log(resultArr, "result"); //['1_id', '1_id', '12_id'] 'result'
As far as time and space complexity goes this solution is much faster, but takes up much more space (in most cases that's a good tradeoff).
Array.include() traverses the entire array and has a time complexity of O(n).
key in object has a constant time complexity, but building the object is O(n) space.
In my opinion the best possible solution would be combining the instant lookup of an object with the scoping of reduce.
const resultArr = data.reduce(
(acc, obj) => {
const id = obj.dxParentId;
const { seen, filtered } = acc;
if (id in seen) return acc;
if (id !== null) {
seen[id] = true;
filtered.push(id);
}
return acc;
},
{ seen: {}, filtered: [] }
).filtered;
console.log(resultArr, "result"); // ['1_id', '12_id'] 'result'
This solution has the same time and space complexity as the flatmap, but the seen object becomes eligible for garbage collection as soon as the reduce method finishes so those potential space issues aren't as large of a concern.
I am looping through an object that contains multiple telephone numbers as keys and arrays with objects as values.
I have written a reduce method that groups all of the schedules together, except for one issue.
if you run the snippet you see that res is:
{trackingNumber: [ [Array] ]}
I need the object to look like:
{trackingNumber: [Array]}
The issue I continue to run into is trying to pop or slice or do anything by initial index makes the first array that is concatted (Object.values(res)) basically it enumerates the first object of that array as the first 7 elements of the value associated with tracking number.
{trackingNumber: [0:string, 1:string, 2:string, 3: {object of strings in 0 1 and 2}]}
Any help would be appreciated.
let todayNewTollFree = [
{paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},],
tracking: "+18003160182"},
{paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},],
tracking: "+18003160182"
},
{
paymentSchedule: [],
tracking: "+12134105385"
},
{
paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},],
tracking: "+18007084605"
},
{
paymentSchedule:[{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},],
tracking: "+18007100629"
}
]
let test = todayNewTollFree.reduce(function (res, obj) {
let key = obj.tracking;
if (res[key]) {
res[key] = res[key].map((key) =>
[key].flat().concat(obj.paymentSchedule)
);
} else res[key] = [obj.paymentSchedule];
for (const tracking in res) {
let values = Object.values(res[key]).flat();
console.log(tracking);
console.log(values);
}
return res;
}, {});
console.log(test)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
The for in loop creates a flat array correctly but every attempt I have to go back and call or assign tracking to the newly created array isn't working.
When adding a new key to the object, you should not place it inside an array literal [...] (as that creates an array whose first element is an array) and should simply assign the array itself to the property. Furthermore, when adding to an array, Array#map is not necessary and Array#concat will do the job.
let todayNewTollFree = [ {paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},], tracking: "+18003160182"}, {paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},], tracking: "+18003160182" }, { paymentSchedule: [], tracking: "+12134105385" }, { paymentSchedule: [{amount:500},{amount:500},{amount:500},{amount:500},], tracking: "+18007084605" }, { paymentSchedule:[{amount:500},{amount:500},{amount:500},{amount:500},{amount:500},], tracking: "+18007100629" } ]
let test = todayNewTollFree.reduce(function (res, obj) {
let key = obj.tracking;
if (res[key]) {
res[key] = res[key].concat(obj.paymentSchedule)
} else res[key] = obj.paymentSchedule;
return res;
}, {});
console.log(test)
So I am pretty new when it comes to Javascript and it is as simple as read a json list with a value of:
{
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
}
What I would like to do is to have both the URL and the amount of numbers etc
"https://testing.com/en/p/-12332423/" and "999"
and I would like to for loop so it runs each "site" one by one so the first loop should be
"https://testing.com/en/p/-12332423/" and "999"
second loop should be:
"https://testing.com/en/p/-123456/" and "123"
and so on depending on whats inside the json basically.
So my question is how am I able to loop it so I can use those values for each loop?
As Adam Orlov pointed out in the coment, Object.entries() can be very useful here.
const URLobj = {
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
};
URLobj.URL.forEach(ob => {
console.log('ob', ob);
const entries = Object.entries(ob)[0]; // 0 just means the first key-value pair, but because each object has only one we can just use the first one
const url = entries[0];
const number = entries[1];
console.log('url', url);
console.log('number', number);
})
You mean something like this using Object.entries
const data = {
"URL": [
{"https://testing.com/en/p/-12332423/": "999"},
{"https://testing.com/en/p/-123456/": "123"},
{"https://testing.com/en/p/-456436346/": "422"}
]
}
data.URL.forEach(obj => { // loop
const [url, num] = Object.entries(obj)[0]; // grab the key and value from each entry - note the [0]
console.log("Url",url,"Number", num); // do something with them
})
let's call your object o1 for simplicity. So you can really go to town with this link - https://zellwk.com/blog/looping-through-js-objects/
or you can just use this code :
for(var i = 0; i < o1.URL.length; i++) {
//each entry
var site = Object.keys(URL[i]) [0];
var value = Object.values(URL[i]) [0];
// ... do whatever
}
don't forget each member of the array is an object (key : value) in its own right
You can extract the keys and their values into another object array using map
Then use the for loop on the newly created array. You can use this method on any object to separate their keys and values into another object array.
const data = {
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
}
var extracted = data.URL.map(e => ({
url: Object.keys(e)[0],
number: Object.values(e)[0]
}))
extracted.forEach((e) => console.log(e))