Sort and Reduce does not retain the sort - javascript

I currently have an object array which I sort and reduce like so:
(The reason I used the below is that I found it in a post sort by alphabetical order keys in js)
const sortedData = Object.entries(templateData)
.sort(([,v1], [,v2]) => +v2 - +v1)
.reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
This produces the following on a console.log(sortedData)
Aguascalientes
:
{productTemplate: {…}, colorVariants: false}
Alabama
:
{productTemplate: {…}, colorVariants: false}
Alaska
:
{productTemplate: {…}, colorVariants: false}
Arizona
:
{productTemplate: {…}, colorVariants: false}
Arizona
:
{productTemplate: {…}, colorVariants: false}
Arkansas
:
{productTemplate: {…}, colorVariants: false}
This is exactly what I want. However the list still appears as if it's NOT in alphabetical order.
When I do a forEach right after the above code like so:
Object.keys(sortedData).forEach((element, idx) => {
console.log(element)
console.log(idx)
});
The list appears in it's original order NOT the sorted reduced one.
What am I missing here?

You can use a transient Map that ensures key order based on insertion :
const data = {
a: 5,
b: 3,
c: 8,
d: 1
};
console.log(data);
const m = new Map([...Object.entries(data)].sort((a, b) => +b[1] - +a[1]));
console.log(Object.fromEntries(m.entries()));

Related

Transform an tab of key values into an array of key values

I would like to transform an tab array of key values into an array of key values
rates.map(rate => {
return {
[rate]: account.vent.filter(v => v.rate === rate)[0]
?.vat,
}
})
(5) [{…}, {…}, {…}, {…}, {…}]
0: {10: 500}
1: {20: 630}
2: {5: undefined}
3: {19.6: undefined}
4: {1: undefined}
I have this in my console I would like something more like:
{
10: 500
20: 630
5: undefined
19.6: undefined
1: undefined
}
Add this and your problems will be solved
.reduce(function(p,c){
return {...p,...c};
},{})
Your code will look like this after adding the above code
rates.map(rate => {
return {
[rate]: account.vent.filter(v => v.rate === rate)[0]
?.vat,
}
})
.reduce(function(p,c){
return {...p,...c};
},{})
It looks like reduce is a more appropriate method to use for your case, since you want to create an object out of an array.
something like
rates.reduce((ratesObj, rate) => {
ratesObj[rate] = account.vent.find(v => v.rate === rate)?.vat;
return ratesObj;
}, {})
Try this
array.reduce((acc, item)=>{
return {...acc, ...item}
},{})
Reduce Method:-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
A good video on reduce method:-
https://www.youtube.com/watch?v=IfieE2G3bfg

Includes method doesnt return true for key extracted from Object.entries of Array

I am trying to filter objects who key's correspond to values in an array of arrays. So 3 sets of objects each filtered by their placement in an array. (Objects are listed in an array.) I suspect this is a two-part question. Why doesn't the Array.prototype.includes method return true, when the case is true?
boardObj = [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
const winCol = (boardObj) => {
const indexes = [[0,1,2], [3,4,5], [6,7,8]];
let result = indexes.filter((ele) => {
for (const [k, v] of Object.entries(boardObj)) {
console.log(ele.includes(0))
console.log(ele.includes(k))
if (ele.includes(k)) {
return v
}
}
})
console.log(result)
}
Property names are always string values:
console.log(typeof Object.keys({0: 42})[0]);
But Array#includes basically performs strict comparison, so string values will never be equal to number values.
You can convert your indexes array to an array of arrays of string values, or convert the property name to a number value.
console.log(
[0,1,2].includes(Number(Object.keys({0: 42})[0]))
);
console.log(
['0','1','2'].includes(Object.keys({0: 42})[0])
);
You could also use Array#some and perform "loose" comparison instead:
const key = Object.keys({0: 42})[0];
console.log([0,1,2].some(e => e == key));

How to order localStorage by KEY ASC?

I save data to localStorage.
To be able to order the localStorage i use milliseconds as key.
(But localStorage doesn't sort or order, so i need to build a array or object that i can sort by key)
var key = Date.now();
var value = {
"id": id,
"name": name
};
//ADD DATA TO OBJECT
localStorage.setItem(key, JSON.stringify(value));
Now i'd like to fetch localStorage and display the data ordered by key asc.
I tried:
//CONSOLE LOG LOCALSTORAGE
Storage {1614866637849: "{"id":"1","name":"A"}", 1614866687890: "{"id":"3","name":"C"}", 1614866642078: "{"id":"2","name":"B"}", length: 3}
//DECLARE NEW OBJ
var items = {};
//LOOP THREW localStorage
Object.keys(localStorage).forEach(function(key){
//FETCH THIS ROUND DATA
items[key] = JSON.parse(localStorage.getItem(key));
});
//CONSOLE LOG ITEMS
1614866637849: {…}, 1614866687890: {…}, 1614866642078: {…}}
//SORT ITEMS
var sorted_items = Object.keys(items).reduce((accumulator, currentValue) => {accumulator[currentValue] = items[currentValue]; return accumulator;}, {});
//CONSOLE LOG SORTED ITEMS
1614866637849: {…}, 1614866687890: {…}, 1614866642078: {…}}
So it looks like my ordering function does nothing?
How can i loop out my data from localStorage by key ASC?
The order i wan't is:
....49
....78
....90
If you want to just print the results in key order, just sort the keys before calling forEach.
Object.keys(localStorage).sort().forEach...
If you want sorted "pairs", because key order is not guaranteed, you can try the following.
const storage = {
1614866637849: '{"id":"1","name":"A"}',
1614866687890: '{"id":"3","name":"C"}',
1614866642078: '{"id":"2","name":"B"}'
};
const pairs = Object.entries(storage)
.map(([key, value]) => ({
key: parseInt(key),
value: JSON.parse(value)
}))
.sort(({ key: keyA }, { key: keyB }) => keyA - keyB);
console.log(pairs);
.as-console-wrapper { top: 0; max-height: 100% !important; }
You can do it this way:
Object.entries(YOUROBJECT).sort().reduce( (o,[k,v]) => (o[k]=v,o), {} );
Your example:
var yourdata = {1614866637849:{"id":"1","name":"A"}, 1614866687890:{"id":"3","name":"C"}, 1614866642078:{"id":"2","name":"B"}, length: 3};
console.log("BEFORE", yourdata);
console.log("NOW", Object.entries(yourdata).sort().reduce( (o,[k,v]) => (o[k]=v,o), {} ));
Result:
1614866637849: {id: "1", name: "A"}
1614866642078: {id: "2", name: "B"}
1614866687890: {id: "3", name: "C"}
Credit goes to:
Sort JavaScript object by key
#sravan ganji
The easiest and smartest way was commented by #Mr.polywhirl
Just add .sort() in the forEach:
Object.keys(localStorage).sort().forEach(function(key){..

RXJS Observables - http call for each index value and merge result

I do a http call to get an Array with objs. And now I want to call for each objs that return me an ID another http call. After all I want to have one observable result.
So far I managed to get for each index a http call. The problem instead of one result I got multiple.
getStats(tag: string) {
return this.service.getClanByClanTag(tag)
.map(clan => {
return clan.memberList; //the arr that return the ID's
})
.switchMap((member: PlayerByMemberListType[]) => {
return member; // singleObj of the arr
})
.concatMap((singleMember) => {
return this.service.getPlayerData(singleMember.tag).map(player => {
//push data to the new arr which should return only one time
this.newArr.push({
tag: singleMember.tag,
name: singleMember.name,
warStars: player.warStars,
trophiesNightBase: singleMember.versusTrophies
});
return this.newArr;
});
});
}
This is what the console prints out after subscribing to it:
Array [ {…} ]
Array [ {…}, {…} ]
Array(3) [ {…}, {…}, {…} ]
Array(4) [ {…}, {…}, {…}, {…} ]
Array(5) [ {…}, {…}, {…}, {…}, {…} ]
...
I know I need some kind of Observable.forkJoin but I don't know how integrate it in the code.
Try something like this:
this.service.getClanByClanTag(tag)
.mergeMap(clan => clan.memberList)
.mergeMap(
member => this.service.getPlayerData(member.tag), // supposedly this returns an observable
(member, player) => ({
tag: member.tag,
name: member.name,
warStars: player.warStars,
trophiesNightBase: member.versusTrophies
})
)
.toArray()
So basically what you want to achieve is this.
Get the clan info
Using clan info from step 1, get the memberList in the clan
For each member inside the memberList, get the players
You will need to think of a way to preserve the info at step2 when before switchMap in step3. Usually we will use a Subject, but in the case if you do not want to, simply map the Observable to preserve the data:
getStats(tag: string) {
return this.service.getClanByClanTag(tag)
.map(clan => {
return clan.memberList; //the arr that return the ID's
})
.switchMap((memberList: PlayerByMemberListType[]) => {
//note that the following map is a function of javascript array, not Observable
//it returns an array
let arrayOfObservables = memberList.map(singleMember => {
this.service.getPlayerData(singleMember.tag)
//map the data so as to preserve the data of singleMember
//by creating a new object, using Object.assign
.map(playerData => {
return Object.assign({memberData: singleMember}, playerData,)
});
})
return Observable.forkJoin(arrayOfObservables);
})
.map(players => {
//players is an array of Object that is the format of {memberData:singleMember, playerData:player)
//perform Object destructuring method
return players.map(({memberData,playerData}) => {
return {
tag: memberData.tag,
name: memberData.name,
warStars: playerData.warStars,
trophiesNightBase: memberData.versusTrophies
}
})
})
}

spread operator (...) is creating extra fields in array in es6

I wanted to embed a new key/value pair in the respective indexed array of objects based on an onChange event.
However, it is done correctly but adding extra elements in the array.
Original array of objects:
0:{data: {…}}
1:{data: {…}}
2:{data: {…}}
3:{data: {…}}
4:{data: {…}}
Achieved result:
0:{data: {…}}
1:{data: {…}}
2:{data: {…}, origin: "UK"}
3:{data: {…}, origin: "UK"}
4:{data: {…}}
5:"UK"
6:"UK"
Intended result:
0:{data: {…}}
1:{data: {…}}
2:{data: {…}, origin: "UK"}
3:{data: {…}, origin: "UK"}
4:{data: {…}}
Below is my code doing it in a loop:
render: (rowData, indexes) => {
return (
<SelectField
id={`origin-${indexes.rowIndex}`}
defaultValue="US"
style={{ position: 'absolute' }}
onChange={text => {
this.setState(
{
generalPermitSelectedVehicles: [
...generalPermitSelectedVehicles,
(generalPermitSelectedVehicles[
indexes.rowIndex
].origin = text),
],
},
() => {
console.log({
generalPermitSelectedVehicles: this.state
.generalPermitSelectedVehicles,
});
},
);
}}
menuItems={[
{
label: 'America',
value: 'US',
},
{
label: 'United Kingdom',
value: 'UK',
},
{
label: 'Oman',
value: 'Oman',
},
]}
/>
);
},
Write it like this:
this.setState(prevState => {
let data = [...prevState.generalPermitSelectedVehicles];
data[indexes.rowIndex].origin = text;
return {generalPermitSelectedVehicles: data};
})
Why its failing in your case?
Because when you do:
[...arr, (arr[index].origin=10)]
It will do two things, first it will update the value of origin at that index, second it will add 10 (returned 10 from ()) at the end of array also.
Check this snippet:
let arr = [{a:1}, {a:2}, {a:3}];
arr = [...arr, (arr[1].a=500)]; //500 will get added in the last
console.log('new value', arr);
Suggestion: Use updater function (prevState) because next state (value) of generalPermitSelectedVehicles is dependent on previous value.
Check the DOC for more details about setState updater function.
You need to update the original state and not append it. You are not using spread operator correctly. Also make use of functional setState when you want to update state based on prevState. You would need to do
this.setState(
prevState => ({
generalPermitSelectedVehicles: [
...prevState.generalPermitSelectedVehicles.slice(0, index.rowIndex),
{...prevState.generalPermitSelectedVehicles[
indexes.rowIndex
], origin: text},
...prevState.generalPermitSelectedVehicles.slice(index.rowIndex + 1)
],
},
() => {
console.log({
generalPermitSelectedVehicles: this.state
.generalPermitSelectedVehicles,
});
},
);
The error in your approach is that you are appending the updated state after spreading the original state, you need to update the existing instead.
Also check this answer on how to update nested state

Categories