Grouping array data according to a id - javascript

With a API call i'm receiving a response like below
[
{
stationId: "10"
name: "Jinbaolai"
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "13"
name: "Stack"
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "20"
name: "Overflow"
group: {id: "19", stationGroupName: "Baba"}
}
]
As you can see first two records consist with the same group. I want to group these data according to the group. So for example it should look like this
[
{
groupId: "18",
groupName : "Ali",
stations : [
{
stationId: "10",
name: "Jinbaolai"
},
{
stationId: "13",
name: "Stack"
}
]
},
{
groupId: "19",
groupName : "Baba",
stations : [
{
stationId: "20",
name: "Overflow"
},
]
}
]
I want to do the grouping logic in my reducer where i also set the full data array that is shown in the beginning of the question.
case EVC_SUCCESS:
return {
...state,
chargingStations: action.evcData.chargingStations,
chargingStationGroups: //This is where my logic should go. ('action.evcData.chargingStations' is the initial data array)
tableLoading: false
}
How can i do this? I tried something using filter but not successful.

The best way to do this is to use Array.prototype.reduce()
Reduce is an aggregating function where you put in an array of something and get a single vaule back.
There may be a starting value as last parameter like I used {}.
The signature is reduce(fn, startingValue) where fn is a function taking two parameters aggregate and currentValue where you return the aggregate in the end.
const groupData = (data)=> {
return Object.values(data.reduce((group,n)=>{
if (!group[n.group.id]){
group[n.group.id] = {
groupId:n.group.id,
groupName: n.group.stationGroupName,
stations:[]}
}
group[n.group.id].stations.push({
stationID: n.stationId,
name: n.name
})
return group;
}, {}))
}
Here is the fiddle

A simple JS algorithm can do that for you
const list = [
{
stationId: "10",
name: "Jinbaolai",
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "13",
name: "Stack",
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "20",
name: "Overflow",
group: {id: "19", stationGroupName: "Baba"}
}
];
const groups = {};
list.forEach((item) => {
const groupId = item.group.id;
const group = groups[groupId] || {groupId: groupId, groupName: item.group.stationGroupName, stations: []};
group.stations.push({stationId: item.stationId, name: item.name});
groups[groupId] = group;
});
const groupedArray = Object.keys(groups).map((groupId) => groups[groupId]);
console.log(groupedArray); // This will be the output you want

I think chaining multiple functions will work.
const stations = [
{
"stationId": 10,
"name": "Jinbaolai",
"group": {"id": "18", "stationGroupName": "Ali"}
},
{
"stationId": 13,
"name": "Stack",
"group": {"id": 18, "stationGroupName": "Ali"}
},
{
"stationId": 20,
"name": "Overflow",
"group": {"id": "19", "stationGroupName": "Baba"}
}
]
const groups = _.chain(stations)
.groupBy((station) => { return station.group.id })
.map((values, key) => {
return {
"groupId": _.first(values).group.id,
"groupName": _.first(values).group.id,
"stations": _.map(values,(value)=>{ return { "stationId": value.stationId, "name": value.name } })
}
})
console.log("groups",groups)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>

Related

filter object value using node js

Is there any way i can filter values which are present inside Object
[
{
id: "1",
name:"animal_image.jpg"
},
{
id: "2",
name:"fish_image.jpg"
},
{
id: "3",
name:"animal_doc.txt"
},{
id: "4",
name:"fish_doc.txt"
},
{
id: "4",
name:"flower_petals.jpg"
},
{
id: "5",
name:"plant_roots.jpg"
},
{
id: "6",
name:"human_image.jpg"
},
]
i want to filter all the name which contain_image.jpg so output look like this
output=
[ "human_image.jpg",
"anima_image.jpg",
"fish_image.jpg"
]
In this code snippet filtredData is an array of objects where the name includes _image.jpg and output is just an array of names containing _image.jpg
const data = [
{
id: "1",
name: "animal_image.jpg"
},
{
id: "2",
name: "fish_image.jpg"
},
{
id: "3",
name: "animal_doc.txt"
}, {
id: "4",
name: "fish_doc.txt"
},
{
id: "4",
name: "flower_petals.jpg"
},
{
id: "5",
name: "plant_roots.jpg"
},
{
id: "6",
name: "human_image.jpg"
},
]
const filtredData = data.filter(el => el.name.includes("_image.jpg"));
console.log(filtredData);
const output = filtredData.map(el => el.name);
console.log(output);
filter & map
const output = arr
.filter(x => x.name.endsWith('_image.jpg'))
.map(x => x.name);

Removing properties from JSON response

I have this JSON (as a string):
[{"product":{"id":"25","age":"35","name":"hp keyboard"},"quantity":1},
{"product":{"id":"9","age":"25","name":"lenovo hero"},"quantity":2}]
How can delete or remove all id & age properties (with their value) from the array of objects, so I can get new array out of it?
You seem to have an array with two JSON strings, but as you present them in your question they are not valid.
I'll assume your array is in fact like this -- run the snippet to see how many backslashes really need to be there:
let jsons = [
JSON.stringify({ product: { id: "25", age:"35", name: "hp keyboard" }, quantity: 1 }),
JSON.stringify({ product: { id: "9", age:"25", name: "lenovo hero" }, quantity: 2 })
];
console.log(jsons);
You should ask yourself why you stick with JSON, and do not just parse them, and then continue without JSON. But, given that you have JSON there, here is how you can remove the two said properties:
let jsons = [
JSON.stringify({ product: { id: "25", age:"35", name: "hp keyboard" }, quantity: 1 }),
JSON.stringify({ product: { id: "9", age:"25", name: "lenovo hero" }, quantity: 2 })
];
let jsons2 = jsons.map(json => {
let { product: { id, age, ...restProduct }, ...rest } = JSON.parse(json);
return JSON.stringify({ product: restProduct, ...rest });
});
console.log(jsons2);
Note how first the JSON is parsed, then a new object is created that lacks the two properties, and finally that is converted back to JSON again.
If you want to only keep some specific properties ("white list") in a flat (non-nested JSON, then proceed like this:
let jsons = [
JSON.stringify({ product: { id: "25", age:"35", name: "hp keyboard" }, quantity: 1 }),
JSON.stringify({ product: { id: "9", age:"25", name: "lenovo hero" }, quantity: 2 })
];
let jsons2 = jsons.map(json => {
let { product: { id, age }, quantity } = JSON.parse(json);
return JSON.stringify({ id, age, quantity });
});
console.log(jsons2);
In case your input is not an array (as hinted in your comments below), but a JSON string as a whole, then proceed as follows:
let json = JSON.stringify([
{ product: { id: "25", age:"35", name: "hp keyboard" }, quantity: 1 },
{ product: { id: "9", age:"25", name: "lenovo hero" }, quantity: 2 }
]);
let json2 = JSON.stringify(
JSON.parse(json).map(({ product: { id, age }, quantity }) =>
({ id, age, quantity })
)
);
console.log(json);
Just iterate over the array using Array.prototype.forEach and delete the unwanted properties:
const arr = [{
"product": {
"id": "25",
"age": "35",
"name": "hp keyboard"
},
"quantity": 1
},
{
"product": {
"id": "9",
"age": 25,
"name": "lenovo hero "
},
"quantity ": 2
}
]
const newArr = JSON.parse(JSON.stringify(arr)); // deep clone arr
newArr.forEach(function(arrEl) {
delete arrEl.product.id;
delete arrEl.product.age
});
console.log(arr); // old array unchanged
console.log(newArr); // new Array with deleted properties

Sort array of objects based on another array

I have a array of makes as follows:
const makes = [
{id: "4", name: "Audi"},
{id: "5", name: "Bmw"},
{id: "6", name: "Porsche"},
{id: "31", name: "Seat"},
{id: "32", name: "Skoda"},
{id: "36", name: "Toyota"},
{id: "38", name: "Volkswagen"}
]
And I want to sort that array based on another array:
const preferred_makes = ['Volkswagen', 'Audi'];
What I do now is as follows:
const preferred_makes = ['Volkswagen', 'Audi'];
const makes = [
{id: "4", name: "Audi"},
{id: "5", name: "Bmw"},
{id: "6", name: "Porsche"},
{id: "31", name: "Seat"},
{id: "32", name: "Skoda"},
{id: "36", name: "Toyota"},
{id: "38", name: "Volkswagen"}
]
const mainMakes = []
const otherMakes = []
makes.map(make => _.includes(preferred_makes, make.name) ? mainMakes.push(make) : otherMakes.push(make))
console.log(mainMakes)
console.log(otherMakes)
But is there any better way? Can I sort makes to show those preferred_makes as first elements of the array?
Here is the fiddle.
A regular array.sort() with a custom comparison function should be able to do this.
const preferred_makes = ['Volkswagen', 'Audi'];
const makes = [
{id: "4", name: "Audi"},
{id: "5", name: "Bmw"},
{id: "6", name: "Porsche"},
{id: "31", name: "Seat"},
{id: "32", name: "Skoda"},
{id: "36", name: "Toyota"},
{id: "38", name: "Volkswagen"}
]
const sorted = makes.slice().sort((a, b) => {
// Convert true and false to 1 and 0
const aPreferred = new Number(preferred_makes.includes(a.name))
const bPreferred = new Number(preferred_makes.includes(b.name))
// Return 1, 0, or -1
return bPreferred - aPreferred
})
console.log(sorted)
You could take an object with the by one incremented indices and take a default value of Infinity for not found names. Then sort by the delta of the values.
var preferred_makes = ['Volkswagen', 'Audi'],
preferred = preferred_makes.reduce((o, k, i) => (o[k] = i + 1, o), {});
array = [{ id: "4", name: "Audi" }, { id: "5", name: "Bmw" }, { id: "6", name: "Porsche" }, { id: "31", name: "Seat" }, { id: "32", name: "Skoda" }, { id: "36", name: "Toyota" }, { id: "38", name: "Volkswagen" }];
array.sort((a, b) => (preferred[a.name] || Infinity) - (preferred[b.name] || Infinity));
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use reduce to make two arrays without the requirement for sorting:
const preferred_makes = ['Volkswagen','Audi'];
const makes = [{id:"4",name:"Audi"},{id:"5",name:"Bmw"},{id:"6",name:"Porsche"},{id:"31",name:"Seat"},{id:"32",name:"Skoda"},{id:"36",name:"Toyota"},{id:"38",name:"Volkswagen"}];
const [mainMakes, otherMakes] = makes.reduce(([a, b], { id, name }) => ((preferred_makes.includes(name) ? a : b).push({ id, name }), [a, b]), [[], []]);
console.log(mainMakes);
console.log(otherMakes);
.as-console-wrapper { max-height: 100% !important; top: auto; }
To make it even faster, you could Set.prototype.has instead of includes:
const preferred_makes = new Set(['Volkswagen','Audi']);
const makes = [{id:"4",name:"Audi"},{id:"5",name:"Bmw"},{id:"6",name:"Porsche"},{id:"31",name:"Seat"},{id:"32",name:"Skoda"},{id:"36",name:"Toyota"},{id:"38",name:"Volkswagen"}];
const [mainMakes, otherMakes] = makes.reduce(([a, b], { id, name }) => ((preferred_makes.has(name) ? a : b).push({ id, name }), [a, b]), [[], []]);
console.log(mainMakes);
console.log(otherMakes);
.as-console-wrapper { max-height: 100% !important; top: auto; }
With lodash you can generate a dictionary of the original index by the car's make (indexByMake) using _.invert() to get an object of { [car make]: original array index }, and map the values back to numbers.
Use _.orderBy() to sort the array, and use the values from indexByMake according to the name:
const preferred_makes = ['Volkswagen', 'Audi'];
const array = [{ id: "4", name: "Audi" }, { id: "5", name: "Bmw" }, { id: "6", name: "Porsche" }, { id: "31", name: "Seat" }, { id: "32", name: "Skoda" }, { id: "36", name: "Toyota" }, { id: "38", name: "Volkswagen" }];
const indexByMake = _.mapValues(_.invert(preferred_makes), Number);
const result = _.sortBy(array, ({ name }) => indexByMake[name]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You can also sort by the Array.indexOf if the index is there otherwise use String.localeCompare. No real need for lodash really:
const makes = [ {id: "4", name: "Audi"}, {id: "6", name: "Porsche"}, {id: "31", name: "Seat"}, {id: "32", name: "Skoda"}, {id: "5", name: "Bmw"}, {id: "36", name: "Toyota"}, {id: "38", name: "Volkswagen"} ]
const list = ['Volkswagen', 'Audi'];
let result = makes.sort((a,b) => {
let i = list.indexOf(a.name)
return i < 0 ? a.name.localeCompare(b.name) : list.indexOf(b.name) - i
})
console.log(result)

Mutate or Make a new Array by replacing a value

I have two arrays (and the length can be in 1000s):
I want my array to replace status to the status of array 2. Here is the output example:
[{
value: 123,
status: 'demo',
type: '...'
},
{value: 2335,
status: 'demo2',
type: 'xxx'
}]
As we can see it needs to get the status from another array and replace it. What are the most possible efficient solutions for this? As this array can be very large. I don't know a good approach to solve this problem.
Length and sort order can be different, I need to replace array1's status by the array2's status,
By linking Array1's status and Array2's id
My actual Data
[
{
"id": "55",
"status": "2",
"type": "COD",
"value": "5038.2",
},
{
"id": "56",
"status": "2",
"type": "COD",
"value": "5398.2",
},
{
"id": "57",
"status": "2",
"type": "COD",
"value": "10798.2",
}
]
Array 2
[
{
"id": "1",
"status": "Awaiting Confirmation",
},
{
"id": "2",
"status": "Confirmed",
},
{
"id": "3",
"status": "Awaiting Shipment",
},
{
"id": "4",
"status": "Awaiting Pickup",
},
{
"id": "5",
"status": "Shipped",
},
{
"id": "6",
"status": "Delivered",
},
{
"id": "7",
"status": "Cancelled",
},
{
"id": "8",
"status": "Refund Requested",
},
{
"id": "9",
"status": "Refunded",
}
Things i have tried...I have used lodash and a for loop to achieve this
const output = [];
for (let i = 0; i < array1.length; i++) {
const statuscode = array1[i].status;
const result = _.find(array2, { id: statuscode });
output.push({
value: array1[i].value,
status: result.status,
type: array1[i].type
});
}
console.log(output);
For high performance, transform one of the arrays to a Map first. Map lookups are very efficient:
const input1 = [{
value: 123,
status: 1,
type: 'COD',
},
{
value: 2335,
status: 2,
type: 'COD',
},
{
value: 222,
status: 3,
type: 'COD',
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map2 = new Map(Object.values(input2).map(({ id, status }) => [id, status]));
const output = input1.map(({ status, ...rest }) => {
const otherStatus = map2.get(status);
return { ...rest, status: otherStatus };
});
console.log(output);
Code readability generally matters more than speed, but if you wanted, you could transform the .map transformation into a for loop as well:
const input1 = [{
value: 123,
status: 1
},
{
value: 2335,
status: 2
},
{
value: 222,
status: 3
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map1 = new Map(Object.values(input1).map(({ value, status }) => [status, value]));
const output = [];
for (let i = 0; i < input2.length; i++) {
const { id, status } = input2[i];
output.push({ value: map1.get(id), status });
}
console.log(output);
A simple for loop would do:
for (let i = 0; i < array1.length; i++) {
array1[i].status = array2[i].status;
}
This of course assumes that the length and the order of the two arrays is the same.
EDIT
Alternative solution using Array.prototype.find and taking into account different lengths and orders.
for (let i = 0; i < array1.length; i++) {
const buffer = array1[i];
buffer.status = array2.find(x => x.id === buffer.status).status;
}
Also, I would highly recommend giving priority to readability over premature optimisation

Normalizr with nested array of objects

I have a nested array of objects like this:
var matchs = [
{
id: 10689,
sport: 'Tennis',
players: [
{
id: 22,
name:'Rafa Nadal',
country: 'Spain',
odds: [
{id: 1, bookie_1: 1.60},
{id: 2, bookie_2: 1.61},
{id: 3, bookie_3: 1.62},
]
},
{
id: 23,
name:'Roger Federer',
country: 'Spain',
odds: [
{id: 4, bookie_1: 2.60},
{id: 5, bookie_2: 2.61},
{id: 6, bookie_3: 2.62},
]
}
]
},
{
id: 12389,
sport: 'Tennis',
players: [
{
id: 45,
name:'Fernando Verdasco',
country: 'Spain',
odds: [
{id: 7, bookie_1: 2.60},
{id: 8, bookie_2: 2.61},
{id: 9, bookie_3: 2.62},
]
},
{
id: 65,
name:'Andy Murray',
country: 'Spain',
odds: [
{id: 10, bookie_1: 1.60},
{id: 11, bookie_2: 1.61},
{id: 12, bookie_3: 1.62},
]
}
]
}
];
I want to use normalizr to simplify array and use with redux. I have read the Normalizr documentation but it has few examples and I do not know what I am doing wrong.
I have tried the following code without success. The result I get is an array with undefined.
import { normalize, schema } from 'normalizr';
const match = new schema.Entity('matchs');
const player = new schema.Entity('players');
const odd = new schema.Entity('odds');
match.define({
player: [player],
odd: [odd]
});
console.log(normalize(matchs, [match]));
I need something like this:
{
result: "123",
entities: {
"matchs": {
"123": {
id: "123",
players: [ "1","2" ],
odds: [ "1", "2" ]
}
},
"players": {
"1": { "id": "1", "name": "Rafa Nadal" },
"2": { "id": "2", "name": "Andy Murray" }
},
"odds": {
"1": { id: "1", "bookie_1": "1.20" }
"2": { id: "2", "bookie_2": "1.21" }
"3": { id: "3", "bookie_3": "1.22" }
}
}
}
I cannot find a straight solution using only normalizr, so my only choice is to pre-format the data before passing to the normalizer.
const preformattedData = data.map(sport => {
const oddArrays = sport.players.map(player => player.odds || []);
return {
...sport,
odds: [].concat.apply([], oddArrays)
}
})
const odd = new schema.Entity('odds')
const player = new schema.Entity('players',
{
odds: [ odd ]
}
)
const sport = new schema.Entity('sports',
{
players: [ player ],
odds: [odd]
}
)
const normalizedData = normalize(preformattedData, [ sport ]);
Demo: https://codesandbox.io/s/20onxowzwn
I think this is what you need
const odd = new schema.Entity('odds');
const player = new schema.Entity('players' , { odds: [ odd]});
const match = new schema.Entity('matchs', {players: [player]});
but the result will be different because your json it is structured like this, I mean, the odds key is child of players, not of matches, therefore the result will be this way.
Just take a look at the console
Here is a solution with latest version of normalizr
const odds = new schema.Entity("odds");
const players = new schema.Entity("players", {
odds: [odds]
});
const matches = new schema.Entity("matches", { players: [players] });
const normalizedData = normalize(data, [matches]);
It would group data in your question as
{
"entities": {
"odds": {
"1": {
"id": 1,
"bookie_1": 1.6
}
},
"players": {
"22": {
"id": 22,
"name": "Rafa Nadal",
"country": "Spain",
"odds": [
1,
2,
3
]
}
},
"matches": {
"10689": {
"id": 10689,
"sport": "Tennis",
"players": [
22,
23
]
}
}
},
"result": [
10689
]
}
You can achieve your desired result by tweaking the process and merge strategies. I don't have time to do the leg work for you, but I explain the approach in detail here:
https://medium.com/#JustinTRoss/normalizing-data-into-relational-redux-state-with-normalizr-47e7020dd3c1

Categories

Resources