I've got an object that i'm trying to map to a react component (using lodash). The current shape of the objects that I get back from my API (firebase) looks like this...
// ex. 1
{
"-Kdkahgiencls0dnh": {
"name": "a name",
"desc": "a description",
"other": "some other guff"
},
"-Ksdfadfvcls0dsnh": {
"name": "another name",
"desc": "another description",
"other": "some more"
},
"-kgoandiencls0dnh": {
"name": "I am a name",
"desc": "I am a description",
"other": "I am some other guff"
}
}
...but, I loose the primary key when i run through _.map()
What i'm trying to do is get my object in the shape of:
// ex. 2
[
{
"id": "-Kdkahgiencls0dnh",
"name": "a name",
"desc": "a description",
"other": "some other guff"
},
{... the next object ...},
{... etc ...}
]
What i'm doing now is getting my data in the componentWillMount lifecycle method like so:
componentWillMount() {
firebaseRef.on('value', snap => {
let data = snap.val() // the whole original object (see ex. 1)
let tempArray = [] // an array to store my newly formatted objects
_.forEach(data, (item, key) => {
// Here's where i'm not really sure what to do.
// I want to use Object.assign to set a new key:value
// That adds "id": "-theobjectsmainkey" to a new object
// then push to my tempArray and finally setState with the
// correctly formatted array of objects.
})
})
}
Ideas? Thoughts? Thanks.
You can use Object.entries(), .map() and object spread
const data = {
"-Kdkahgiencls0dnh": {
"name": "a name",
"desc": "a description",
"other": "some other guff"
},
"-Ksdfadfvcls0dsnh": {
"name": "another name",
"desc": "another description",
"other": "some more"
},
"-kgoandiencls0dnh": {
"name": "I am a name",
"desc": "I am a description",
"other": "I am some other guff"
}
}
let res = Object.entries(data).map(([id, prop]) => ({id, ...prop}));
console.log(res);
Lodash's _.map() callback receives as a 2nd parameter the iterated key. Use object assign, to create a new object with the key as id:
const array = _.map(data, (item, id) => Object.assign({ id }, item))
Demo:
const data = {"-Kdkahgiencls0dnh":{"name":"a name","desc":"a description","other":"some other guff"},"-Ksdfadfvcls0dsnh":{"name":"another name","desc":"another description","other":"some more"},"-kgoandiencls0dnh":{"name":"I am a name","desc":"I am a description","other":"I am some other guff"}};
const array = _.map(data, (item, id) => Object.assign({ id }, item));
console.log(array);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Here you go, using only pure JS :
const raw = {
"-Kdkahgiencls0dnh": {
"name": "a name",
"desc": "a description",
"other": "some other guff"
},
"-Ksdfadfvcls0dsnh": {
"name": "another name",
"desc": "another description",
"other": "some more"
},
"-kgoandiencls0dnh": {
"name": "I am a name",
"desc": "I am a description",
"other": "I am some other guff"
}
}
let formatted = Object.keys(raw).map(
key=>Object.assign(raw[key], {"id": ""+key})
);
Here is a fiddle to get a live demo.
componentWillMount() {
firebaseRef.on('value', snap => {
let data = snap.val() // the whole original object (see ex. 1)
let tempArray = Object.keys(data).map((item, key) => {
return {
"id": item,
"name": data[item].name // etc, a structure what you want
...
};
})
})
}
Related
I'm using knockoutjs, but the question is really in Javascript domain.
I have variable vm.filteredSerivces() which contains all services by all employees.
Now, I want to just preserve those filteredSerivces where is vm.filteredSerivces()[0].GroupedServices[x].EmployeeId == 3684 (x is the number of index number of each object in GroupedServices object list)
I tried as follows:
var filteredSrvcs = vm.filteredSerivces()[0].GroupedServices.filter(x => x.EmployeeId != Id).remove();
vm.filteredSerivces(filteredSrvcs );
But I changed structure in that way, and my bindings in html is not corresponding.
Is there any other way to just remove this sub-sub object, and to preserve a structure as it is?
Here is the
Here's an example that maps a new array of new objects and the filter is set to only include the GroupedServices items where Id == 2000
let res = data.map(({ServiceTypeName, GroupedServices}) =>{
GroupedServices= GroupedServices.filter(({Id}) => Id == 2000);
return {ServiceTypeName,GroupedServices }
})
console.log(res)
<script>
let data =
[
{
"ServiceTypeName": "Type 1",
"GroupedServices": [{
"Id": 1,
"Name": "A"
}, {
"Id": 2,
"Name": "A"
},
{
"Id": 28456,
"Name": "AGSADGJS"
}]
},
{
"ServiceTypeName": "Type 2",
"GroupedServices": [{
"Id": 1203,
"Name": "AHASJ"
}, {
"Id": 2000,
"Name": "AHSJD"
},
{
"Id": 284536,
"Name": "UEHNCK"
}]
}];
</script>
How can we push values to an object from inside a map function and return that single object. I have string comparison condition inside the map function. I tried using Object.assign but it returns an array with multiple object inside that array. Instead of this multiple object I'm expecting a single object inside an array.
Map function
let arrayObj = arrayToTraverse.map(function(item) {
var myObj = {};
if(item.inputvalue === 'Name'){
Object.assign(myObj, {name: item.value});
} else if (item.inputvalue === 'Email'){
Object.assign(organizerInfo, {email: item.value});
} else if (item.inputvalue === 'Company'){
Object.assign(organizerInfo, {company: item.value});
}
return myObj;
});
console.log("The array object is", arrayObj)
This return the array of objects as follows
[
{
"name": "Tom"
},
{
"email": "tom#abc.com"
},
{
"company": "ABC"
}
]
But The array I'm expecting is
[
{
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
// or
[
"returned": {
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
An example of arrayToTraverse can be considered as following
[
{
"id": "1",
"inputvalue": "Name",
"value": "Tom",
"type": "Short Text"
},
{
"id": "2",
"inputvalue": "Email",
"value": "tom#abc.com",
"type": "Email ID"
},
{
"id": "3",
"inputvalue": "Company",
"value": "Google",
"type": "Long Text"
}
]
Simply put, you're trying to reduce an array to a single object, not map one array to another.
var arrayToTraverse = [
{inputvalue:"Name",value:"Tom"},
{inputvalue:"Email",value:"tom#abc.com"},
{inputvalue:"Company",value:"ABC"},
{inputvalue:"Foo",value:"Bar"} // wont show up
];
var valuesRequired = ["Name","Email","Company"];
var result = arrayToTraverse.reduce( (acc, item) => {
if(valuesRequired.includes(item.inputvalue))
acc[item.inputvalue.toLowerCase()] = item.value;
return acc;
}, {});
console.log(result);
Edit: Added lookup array for required fields.
This question already has answers here:
Does ES6 introduce a well-defined order of enumeration for object properties?
(3 answers)
Closed 3 years ago.
Since it is not possible to get a JSON file with the original order, I'd like to sort it in JS again. The file looks like this:
{
"index": 5,
"timestamp": 1570438008,
"data": {
"12": [
"Title 2",
"Description 2"
],
"10": [
"Title 1",
"Description 1"
]
}
}
If I access this JSON file now from JS, I get another order than the original:
$.ajax({
url: '../json/smiirl_data.json',
dataType: 'json',
success: function(response) {
console.log(response['data']);
}
});
console.log from JS shows:
{
"10": [
"Title 1",
"Description 1"
],
"12": [
"Title 2",
"Description 2"
]
}
Is it possible to sort (title descending) it? I need the orignal order (12, 10).
at first you need to serialize object to array after that you use sort function to sort it.
var array = [],
data = response['data'];
for (var array in data ) {
array.push([array, data[vehicle]]);
}
array.sort(function(a, b) {
return b[1] - a[1];
});
Because objects are not always guaranteed to maintain its order, and JSON does not support the Map class which guarantees the order, you should use an array instead, where there are two elements: the key and the value.
For example:
"data": [
[12, ["Title 2", "Description 2"],
[10, ["Title 1", "Description 1"]
]
The you can access it like:
for(let item of myJSON.data)
{
console.log('Key:', item[0]);
console.log('Value:', item[1]);
}
To guarantee order, re-structure your data to be a 2-dimensional array.
You can create an index map, and lookup function to make the data easier to query.
let data = {
"index": 5,
"timestamp": 1570438008,
"data": [
[ "12", [ "Title 2", "Description 2" ] ],
[ "10", [ "Title 1", "Description 1" ] ]
]
}
let indexMap = indexLookupMap(data.data); // Build an index
console.log(lookupEntry(data.data, indexMap, '10')); // Find entry with id '10'
data.data.forEach(entry => {
console.log(`key => ${entry[0]}, value => ${entry[1].join(', ')}`);
})
function lookupEntry(data, indexMap, id) {
return data[indexMap[id]][1];
}
function indexLookupMap(data) {
return data.reduce((dict, entry, idx) => Object.assign(dict, { [entry[0]]: idx }), {});
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
I want to create flexible table filtering on user-company-roles objects in JS array. User shall be able to provide filtering on multiple object properties, with multiple values, with use of AND (&&) operand when matching.
I would appreciate tip on how to proceed implementing the logic here. Of course I could loop through the array and have multiple nested if-statements, but maybe there are more clean and nice approach on this?
User-Company-Roles Array
const userArray = [
{
"id": "peterpan",
"name": "Peter pan",
"company": [
{
"id": "12345678",
"name": "Company A",
"roles": [
"Role A",
"Role B",
"Role C"
]
}
],
"systemRoles": [
{
"systemName": "System A",
"role": "Admin"
},
{
"systemName": "System B",
"role": "User"
}
]
},
{
"id": "robinhood",
"name": "Robin Hood",
"company": [
{
"id": "9876543",
"name": "Company B",
"roles": [
"Role A"
]
},
{
"id": "546372",
"name": "Company C",
"roles": [
"Role A"
]
}
],
"systemRoles": [
{
"systemName": "System B",
"role": "User"
}
]
},
{
"id": "biggiant",
"name": "Big Giant",
"company": [
{
"id": "546372",
"name": "Company C",
"roles": [
"Role A"
]
}
],
"systemRoles": [
{
"systemName": "System B",
"role": "User"
}
]
}
];
Filter object
const filter = {
includeUserIds: [], // filters 'user.id' that matches values in this array
includeCompanyNames: [], // filters 'user.company.name' that matches values in this array
includeSystemRoleNames: [], // filters 'user.systemRoles.role' that matches values in this array
includeCompanyRoles: [], // filters 'user.company.roles' that matches values in this array
excludeSystemRoleNames: [], // filters 'user.systemRoles.role' that **DOES NOT** match values in this array
excludeCompanyRoles: [] // filters 'user.company.roles' that **DOES NOT** match values in this array
}
Matching
When filtering the array I want the filter to match user-objects as this (pseudo code):
filter.includeUserIds && filter.includeCompanyNames
&& filter.includeSystemRoleNames && filter.includeCompanyRoles
&& !filter.excludeSystemRoleNames && !filter.excludeCompanyRoles
Example 1: Filter users by user id:
const filter = {
includeUserIds: ['peterpan', 'robinhood'],
includeCompanyNames: [],
includeSystemRoleNames: [],
includeCompanyRoles: [],
excludeSystemRoleNames: [],
excludeCompanyRoles: []
}
Would return array with Peter Pan and Robin Hood users
Example 2: Filter by company name
const filter = {
includeUserIds: [],
includeCompanyNames: ['Company C'],
includeSystemRoleNames: [],
includeCompanyRoles: [],
excludeSystemRoleNames: [],
excludeCompanyRoles: []
}
Would return array with Robin Hood and Big Giant users
Example 3: Filter by company roles and system roles
const filter = {
includeUserIds: [],
includeCompanyNames: [],
includeSystemRoleNames: ['User'],
includeCompanyRoles: ['Role A'],
excludeSystemRoleNames: ['Admin'],
excludeCompanyRoles: []
}
Would return array with Robin Hood and Big Giant users
I would create a decision function for each type of the filter and put it into object with same properties as filter is. Each function should accept 2 arguments: item of type any and filter of type string[]. So it will look like:
const deciders = {
includeUserIds: (item, filter) => { ... },
includeCompanyNames: (item, filter) => { ... },
includeSystemRoleNames: (item, filter) => { ... },
includeCompanyRoles: (item, filter) => { ... },
excludeSystemRoleNames: (item, filter) => { ... },
excludeCompanyRoles: (item, filter) => { ... },
};
Then filtering is a simple filter of original array like:
const filtered = userArray.filter(user => {
let include = true;
Object.keys(deciders).forEach(type => include &= deciders[type](user, filter[type]));
return include;
});
I have a JSON response that looks like this:
{
"category1": [
{
"Description": "Insert description here",
"Title": "Title #1"
}
],
"category2": [
{
"Description": "Insert description here",
"Title": "Title #2"
},
{
"Description": "Insert description here",
"Title": "Title #3"
}
]
}
How can I get a random item from this response by reading through both categories?
My JSFiddle here works for Category 2. But I don't know how to read both categories: http://jsfiddle.net/uzpfxdgp/
var mapped = Object.keys(json).map(function(key, index){ return json[key]; })
.reduce(function(a, b) { return a.concat(b); });
var index = Math.floor(Math.random() * mapped.length);
console.log(mapped[index]);
Take a look at this jsFiddle that I created. Is that what you are looking for?
http://jsfiddle.net/Arllo/q66wkkmx/
var list = {
"category1": [
{
"Description": "Insert description here",
"Title": "Title #1"
}
],
"category2": [
{
"Description": "Insert description here",
"Title": "Title #2"
},
{
"Description": "Insert description here",
"Title": "Title #3"
}
]
};
var numberOfProperties = Object.keys(list).length;
var randomProperty = Math.floor((Math.random() * numberOfProperties) + 1);
var randomCategory = "category" + randomProperty;
console.log(list[randomCategory]);
I didn't test this, but I think this might do the trick (where myObj is the object you included above).
var categoryArr = [];
for (var category in myObj) {
categoryArr.push(myObj[category])
}
var randomObj = categoryArr[Math.floor(Math.random() * categoryArr.length)];