get Object data from Firestore with order - reactjs

I'm trying to get object data from firestore
the problem is that although I saved the data as
command, damage, frame, state, way
when I get the data from firestore in React,
the order of the object is shuffled.
For example,
frame, range, command, damage, hitframe
range, hitframe, frame, command, damage,
etc...
I want it to be orderd as command, damage, frame, state, way

Here’s how you can sort an object by its keys/properties, alphabetically:
<script>
const unordered = {
'frame': 'foo',
'range': 'bar',
'command': 'baz',
'damage': 'hit',
'hitframe': 'no'
};
console.log(JSON.stringify(unordered));
// → '{'frame': 'foo', 'range': 'bar','command': 'baz','damage': 'hit','hitframe': 'no'}'
const ordered = Object.keys(unordered).sort().reduce(
(obj, key) => {
obj[key] = unordered[key];
return obj;
},
{}
);
console.log(JSON.stringify(ordered));
// → '{"command":"baz","damage":"hit","frame":"foo","hitframe":"no","range":"bar"}'
</script>
In this way, you will be able to get your objects in alphabetical order. Mention if you need anything else.

The data is saved as array and as such sorted by the array index. I would recommend thatyou just sort the data on client side when receiving it using the JavaScript sort array.

Related

Cannot return documents based off a sorted index using Fauna DB

I'm bumbling my way through adding a back-end to my site and have decided to get acquainted with graphQL. I may be structuring things totally the wrong way, however from following some tutorials I have a React front-end (hosted on Vercel), so I have created an api folder in my app to make use of Vercel's serverless functions. I'm using Apollo server and I decided to go with Fauna as my database.
I've successfully been able to return an entire collection via my API. Now I wish to be able to return the collection sorted by my id field.
To do this I created an index which looks like this:
{
name: "sort_by_id",
unique: false,
serialized: true,
source: "my_first_collection",
values: [
{
field: ["data", "id"]
},
{
field: ["ref"]
}
]
}
I then was able to call this via my api and get back and array, which simply contained the ID + ref, rather than the associated documents. I also could only console log it, I assume because the resolver was expecting to be passed an array of objects with the same fields as my typedefs. I understand I need to use the ref in order to look up the documents, and here is where I'm stuck. An index record looks as follows:
[1, Ref(Collection("my_first_collection"), "352434683448919125")]
In my resolvers.js script, I am attempting to receive the documents of my sorted index list. I've tried this:
async users() {
const response = await client.query(
q.Map(
q.Paginate(
q.Match(
q.Index('sort_by_id')
)
),
q.Lambda((ref) => q.Get(ref))
)
)
const res = response.data.map(item => item.data);
return [... res]
}
I'm unsure if the problem is with how I've structured my index, or if it is with my code, I'd appreciate any advice.
It looks like you also asked this question on the Fauna discourse forums and got an answer there: https://forums.fauna.com/t/unable-to-return-a-list-of-documents-via-an-index/3511/2
Your index returns a tuple (just an array in Javascript) of the data.id field and the ref. You confirmed that with your example result
[
/* data.id */ 1,
/* ref */ Ref(Collection("my_first_collection"), "352434683448919125")
]
When you map over those results, you need to Get the Ref. Your query uses q.Lambda((ref) => q.Get(ref)) which passes the whole tuple to Get
Instead, use:
q.Lambda(["id", "ref"], q.Get(q.Var("ref")))
// or with JS arrow function
q.Lambda((id, ref) => q.Get(ref))
or this will work, too
q.Lambda("index_entry", q.Get(q.Select(1, q.Var("index_entry"))))
// or with JS arrow function
q.Lambda((index_entry) => q.Get(q.Select(1, index_entry)))
The point is, only pass the Ref to the Get function.

Why doesn't useState work with deeply nested objects and arrays in them?

In my use case I have an array of characters, each character has multiple builds, and each build has a weapons string, and artifacts string. I'm making a tool to select portions of each string and assign them to a value, e.g. assigning index 3-49 of weapons to a specific weapon.
const [characterIndices, setCharacterIndices] = useState<
{ builds: { weaponIndices: SE[]; artifactSetIndices: SE[] }[] }[]
>([
...characters.map((char) => {
return {
builds: [
...char.builds.map((_build) => {
return {
weaponIndices: [],
artifactSetIndices: [],
};
}),
],
};
}),
]);
The SE type is as follows:
type SE = { start: number; end: number; code: string };
//start and end are the respective start and end of selected text
//code is the specific artifact or weapon
The weaponIndices and artifactSetIndices basically hold the start and end of selected text in a readonly textarea.
I have a function to add a SE to either weaponIndices or artifactSetIndices:
const addSE = (
type: "weaponIndices" | "artifactSetIndices",
{ start, end, code }: SE,
characterIndex: number,
buildIndex: number
) => {
let chars = characterIndices;
chars[characterIndex].builds[buildIndex][type].push({ start, end, code });
setCharacterIndices((_prev) => chars);
console.log(characterIndices[characterIndex].builds[buildIndex][type]);
};
I think that using a console log after using a set function isn't recommended, but it does show what it's intended to the weaponIndices, or artifactSetIndices after an entry is added.
Passing the addSE function alongside characterIndices to a separate component, and using addSE, does print the respective indices after adding an entry, but the component's rendering isn't updated.
It only shows up when I "soft reload" the page, when updating the files during the create-react-app live reload via npm run start.
In case you are confused about what the data types are, I've made a github repo, at https://github.com/ChrisMGeo/ght-indexer/tree/main/src at src/data.json. That JSON file describes what the character data looks like, including the builds, and each build's weapons and artifacts(called artifact_sets in the JSON)
Looks to me you are not updating the state at all.
Here you are just storing the same object reference that you already have in state into a new variable chars.
let chars = characterIndices;
chars now holds reference to a same object as characterIndices.
Here you are mutating that same object
chars[characterIndex].builds[buildIndex][type].push({ start, end, code });
And here you are updating the state to the same object that is already in the state. Notice that no state update here occurs.
setCharacterIndices((_prev) => chars);
Object you have in state is mutated, but you did not "change" the value of the state, thus no component re-render.
What you could maybe do is create a copy of the object, mutate that and update the state. just change chars assignment like this:
let chars = {...characterIndices};
React often compares values using Object.is() only to a single level of nesting (the tested object and its children).
It will not re-render if the parent is found equal, or if all the children are found equal.
React then considers that nothing has changed.
In your implementation, even the first top-level check will immediately fail, since Object.is(before, after) will return true.
You could use an Immutable objects approach to eliminate this concern when setting a new state (either directly through spreading values or with a support library such as Immer).
For example instead of setting the values within the object...
myObj.key = newChildObj
...you would make a new object, which preserves many of the previous values.
myObj === {...myObj, key: newChildObj}
This means that every changed object tree is actually a different object (with only the bits that haven't changed being preserved).
To read more about this see https://javascript.plainenglish.io/the-effect-of-shallow-equality-in-react-85ae0287960c

React.js: How to properly append multiple items to an array state using its use-state hook setter?

My component has a state that should store an array of objects. I initialized it to be empty.
const [tableDataSource, setTableDataSource] = useState([])
After a couple successful fetching and reorganizing of fetch results, one or more object literals should be appended to the tableDataSource state. Desired output should be
[
{
key: 1,
key2: 'sample string value',
key3: false,
},
{
key: 'sample string value',
key2: true,
}
]
My current fetch result is another array containing 2 object literals. I tried to append all items from this array to the state setter.
let dataSource = generateRows(showCompletedItems, fetchResult);
The code above reorganizes the fetch results into desired output. I logged dataSource in the console and verified if it is an Array object and it returned true.
Somewhere inside the component declaration I also have a useEffect that logs the current tableDataSource to output the changes.
console.log('TABLE DATA SOURCE >>>>', tableDataSource, typeof tableDataSource);
I have a difficult time appending the array items into the state array using its setter. I tried Approach # 1:
setTableDataSource(dataSource);
The console does not log anything.
Approach # 2:
setTableDataSource((tableDataSource) => [...tableDataSource, dataSource])
Returns a nested array as shown below:
[[{...},{...}]]
If I used Approach # 2 and initialized the component state like this:
const [tableDataSource, setTableDataSource] = useState([{}])
Result:
[{}, [{...}, {...}]]
Approach # 3 does not log anything like the first one
setTableDataSource((tableDataSource) => [...tableDataSource, ...dataSource])
Figured it out.
setArrayOfObjects((currentObjects) => currentObjects.concat([ ...anotherArrayOfObjects]))
I hope this very specific problem will help others. It was very annoying, it took me hours to figure it out.
You can use Set.
setTableDataSource((tableDataSource) => [... new Set([...tableDataSource, ...dataSource])])
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

How to order a dictionary by value in AngularJS

I've a REST api that returns the list of locales as dictionary:
{
"en-uk": "English UK",
"en-us": "English USA",
...
}
This dictionary is correctly ordered alphabetically by value.
When AngularJS receives it via HTTP, the dictionary gets automatically re-sorted by key, so when I bind to a select element the list of options is ordered by key, and the alphabetical order by key doesn't match the one by value, I get a wrong sorting.
The problem I suppose is due to the fact that such dictionary becomes basically one object with 800+ properties. How do I sort it by value?
First: You have to find all keys.
Second: Iterate all the keys.
Third: Then sort the array with values.
Please use the following:
let obj = {
"en-us": "English USA",
"en-uk": "English UK"
};
// Get an array of the keys:
let keys = Object.keys(obj);
// Then sort by using the keys to lookup the values in the original object:
keys.sort(function(a, b) { return obj[a] > obj[b] });
console.log(keys);
console.log(obj[keys[0]]);
You can modify the way you send the response from the server. Instead of sending the response as an object, send the stringified object.
The problem is indeed you cannot sort the values of the properties of an object. So I convert it to an array before binding it:
So,
languageResource.getCultures().then(function(cultures) {
vm.availableCultures = cultures;
});
becomes
languageResource.getCultures().then(function (culturesDictionary) {
var cultures = [];
angular.forEach(culturesDictionary, function (value, key) {
cultures.push({
lcid: key,
name: value
});
});
vm.availableCultures = cultures;
});
Seen this when the key is numerical. If the key's data type is string than it would keep its sorted state after an API call. If the key's data type is numerical, than, you would need set the key's value as a string and even add single quotes before and after the key's value, before the API sends it back.
I haven't tried the approach to stringfy the dictionary in the API. After the call you would parse the string back to an object with something like JSON.parse(string) might be your best bet.

MongoDb subdocument array populate (via Mongoose ORM) : Does it maintain array order when populate is called

Suppose I have 2 Schema's in Mongoose that look like this:
var movieSchema = mongoose.Schema({
name: String,
type: String
});
var moviePlaylistSchema = mongoose.Schema({
name: String,
movies: [{type: mongoose.Schema.Types.ObjectId, ref: 'Movie'}]
});
var Movie = mongoose.model('Movie', movieSchema);
var MoviePlaylist = mongoose.model('MoviePlaylist', moviePlaylistSchema);
If a query was made along the following lines:
MoviePlaylist.find({}).populate('movies').exec(function(err, res) {
if (err) console.log('err', err);
else {
console.log('res', res);
res.forEach(function(elem, index) {
console.log('elem.name', elem.name);
});
}
});
Would the order of the elements in the array be maintained? The objective here is to allow the user to maintain a playlist order of their movies. If, when the "populate" method fires, the array order of Movie object Ids is not maintained, then this will not serve my purpose. Hence thought I'd ask someone who is more knowledgeable in this area.
If this works, then I have another task which is allowing the user to change the order of movies in the playlist, which should be straight forward by allowing the movie object id index to be swapped in the array.
Thanks for your help in advance.
MongoDB will keep the order of the array, much like an array in any programming language.
You can view the BSON/JSON spec for reference which highlights that the array must contain integer values for keys, and be maintained in ascending numerical order.
Additionally, the Mongoose populate on an array works by calling Model.populate via forEach on each element of the array. This modifies the array in place, hence the order is preserved. You can see the relevant source code here.

Resources