JavaScript - Creating Object with specific format out of 2 Arrays - javascript

I have 2 Arrays.
The Array "people" just holds basic information of some people.
The Array "subscriptions" has a lot of different subscriptions to games.
I want to have have an Array, where I can sort of have an Overview of what game subscriptions each person has. This is not real code I use, I am just trying to get used to JavaScript.
an example Element of an Array called people:
{"number":4251,"name":"Alex","surname":"Scott"}
an example Element of an Array called subscriptions:
{"number":4329,game:"Tetris"}
I want to make a new new Array with the following format:
person: (people[i]), subscriptions: [(subscriptions[j], subscriptions[j+k], ...)]
What I tried:
const array3 = people.map(x => {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)});
It get this Error:
SyntaxError: Unexpected token :
How can I insert multiple key value pairs in in these Objects?

This happens because your argument in the map is interperting the { as opening bracket of the method body.
const arr = [{some: 1, object: 2}, {some:3, object:4}]
arr.map((o) => {original: o, cool: 'yes'})
To resolve this you have to explicitly return the object or add additional parenthesis to let the interperter know that this is not the method body:
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => {return {original: o, cool: 'yes'}})
console.log(mapped)
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => ({original: o, cool: 'yes'}))
console.log(mapped)
I hope this helps!

In your original code there is a syntax error, when it comes to the mapping part. Either you go with the long version of this command or, since you directly want to return the element, you can use the short version:
const people = [{"number":4251,"name":"Alex","surname":"Scott"}, {"number": 4329, "name":"Mr", "surname": "Tetri"}]
const subscriptions = [{"number":4329,game:"Tetris"}, {"number":4329, game:"Solitaire"}, {number: 4251, game: "Tetris"}]
// using an anonymous function including full body
const arrayLongVersion = people.map(x => { return {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)} });
// using an anonymous function with direct return of elements
const arrayShortVersion = people.map(x => ({person: x , subscriptions: subscriptions.filter(y => y.number === x.number)}))

Here you go
const people = [
{"number":1,"name":"Alex","surname":"Scott"},
{"number":2,"name":"John","surname":"Anderson"},
{"number":3,"name":"Jacob","surname":"Sanderson"},
{"number":4,"name":"Lionel","surname":"Messi"},
{"number":5,"name":"Cristiano","surname":"Ronaldo"}
];
const subscriptions = [
{"number":1,game:"Tetris"},
{"number":2,game:"Super Mario"},
{"number":3,game:"Fortnite"},
{"number":4,game:"FIFA"},
{"number":5,game:"Zelda"}
];
const peopleSubscriptions = people.map(person => {
return {
...person,
subscriptions: subscriptions.filter(sub => sub.number === person.number)
}
});
console.log(peopleSubscriptions);

Related

How to iterate through enums with integer as value in Typescript?

I have an enum
export enums Actions {
All = 1,
Success = 2,
Failed = 3
}
When I iterate through it using for loop, I am getting total of 6 entries. I get to know that this is how it works in Typescript. But how can I use the enum so that I can access
enum.key
for "All, Success, Falied" and
enum.value
for 1,2,3
As per the docs of typescript, if as const keyword suffices, then we dont need to use enum.
Just interpreting, maybe in this case an implementation like illustrated can be used, with Object.keys(obj) and Object.values(obj) to get the required outputs
const Actions = {
All: 0,
Success: 1,
Failure: 2
} as const
let keyArr = Object.keys(Actions);
let valArr = Object.values(Actions);
What you can do in order to access the enum keys and values like you described in the OP is convert your enum to a basic object that has the properties keys and values with the corresponding data.
export enum Actions {
All = 1,
Success = 2,
Failed = 3
}
export type ConvertedActions = {
keys: string[];
values: Actions[];
};
const result = Object.values(Actions).reduce(
(acc, curr): ConvertedActions =>
isNaN(+curr)
? {...acc, keys: [...acc.keys, curr as string]}
: {...acc, values: [...acc.values, curr as Actions]},
<ConvertedActions>{keys: [], values: []}
);
console.log(result.keys); // [ 'All', 'Success', 'Failed' ]
console.log(result.values); // [ 1, 2, 3 ]
Note: Don't get confused by the extra type (ConvertedActions) I defined. However, it is needed to avoid using any a lot.
const keys = Object.keys(Actions);
const values = Object.values(Actions);
What you can do, in this specific case, is separating All, Success, Falied and 1, 2, 3 in two different arrays.
export enums Actions {
All = 1,
Success = 2,
Failed = 3
}
console.log(Object.keys(enums).filter((v) => isNaN(Number(v)))); // ["All", "Success", "Failed"]
console.log(Object.keys(enums).filter((v) => !isNaN(Number(v)))); // ["1", "2", "3"]
You can also do it with a for..in loop:
for (const value in enums) {
console.log(value); // 1, 2, 3, All, Success, Falied
console.log(value.filter((v) => isNaN(Number(v)))); // All, Success, Falied
console.log(value.filter((v) => !isNaN(Number(v)))); // 1, 2, 3
}
There are more ways to do it using forEach, for..of, etc. But from your question I think my example should do the trick.

How to map this type of API data?

I'm trying to map a data from API, but I have some problems with it. How can I push only names to array? There is no property called "name" or something like this.
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) => arr.push(data),
this.setState({
data: arr
})
);
}
{this.state.data.map((person) => {
return <li>{person}</li>
})}
Description
Okay, so having looked at the data returned from that URL, it returns an object, therefore, a simple way would be to use Object.keys. As you can see, I'm using the map function to produce a new array from the array produced by Object.keys, from there passing the array produced from the map function into the log function, just for a little demo.
I'm not saying this is the most efficient/performance friendly way ever, however it is a clean, simple, clear & concise way to do things.
const url = `https://contact-browser.herokuapp.com/contacts`;
const log = args => console.log(args);
fetch(url).then(r => r.json()).then(d => log(Object.keys(d).map(k => d[k])));
Edit
As mentioned in the comments, if you want to be more efficient and you don't care about the keys, you could just use Object.values, with this approach it's just less data to deal with, less processing, etc.
const url = `https://contact-browser.herokuapp.com/contacts`;
const log = args => console.log(args);
fetch(url).then(r => r.json()).then(d => log(Object.values(d)));
Looking at the data source it sort of looks like this:
{
"1": "Jason Brown",
"2": "Angela Houston",
"3": "Angela Maynard",
"4": "Natasha West",
"5": "Christina Lee",
"6": "Bryan Baker",
//other stuff
}
You can try the following:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.values(data)
})
);
}
Note that Object.values, Object.entries, Object.keys, for ... in and the sorts are not guaranteed return data in any order so if you want to order by the key you can do the following:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.entries(data)
.map(([key,val])=>[Number(key),val])//make key a number
.sort(([a],[b])=>a-b)//sort by ket
.map(([,val])=>val)//do not use the key for data
})
);
}
However; when you render your data array you need a unique key or React will give you a warning. To prevent this warning you can provide the object key with your data:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.entries(data)
.map(([key,val])=>({key:Number(key),val}))//make key a number
.sort((a,b)=>a.key-b.key)//sort by key
})
);
}
{this.state.data.map(({key,val}) => {
return <li key={key}>{val}</li>
})}

How do I turn the values from an object into an array?

I have a function that is polling for temperature data:
{"a":"43",
"b":"43",
"c":"42",
"d":"43",
"e":"40",
"f":"41",
"g":"100",
"h":"42.6"}
I want to be able to graph that data over time, but I can't figure out the best way to map the above data, to something like the below data:
temps: [{
name: "a",
data: ["43","42","43"]
},
name: "b",
data: ["43","42","43"]
},
etc...
]
I have tried the code below, and tried to figure out the javascript map function, but I keep running into scoping problems where "this" isn't the same thing as it was in the parent:
this.temp_names.forEach(function(e){
if(typeof this.temps[e] == "undefined") {
this.temps[e] = []
}
this.temps.e.unshift(this.sys_telemetry.S.temps)
if (this.temps.e.length > 10) {
this.temps.e.pop()
}
})
where "temp_names" was an array of the keys.
I'm doing this in VueJS, so the "this" is accessing the data in my component.
Using Array#from, Object#entries, Array#map and destructuring you could do something like this.
const data={"a":"43","b":"43","c":"42","d":"43","e":"40","f":"41","g":"100","h":"42.6"}
const res = Object.entries(data)
.map(([name, data])=>({name, data:[data]}));
console.log(res);
Alternative using Array#reduce, Map,
const data={"a":"43","b":"43","c":"42","d":"43","e":"40","f":"41","g":"100","h":"42.6"}
const res = Array.from(Object
.entries(data)
.reduce((a,[k,v])=>{
if(!a.has(k)) a.set(k, []);
a.get(k).push(v);
return a;
}, new Map()))
.map(([name, data])=>({name, data}));
console.log(res);
graph that data over time
Because you want to do this over time, it would make sense to create an array and then using Object.entries, & Array.find, update the results.
Here is an example.
const values1 =
{"a":"43", "b":"43", "c":"42", "d":"43", "e":"40", "f":"41",
"g":"100", "h":"42.6"};
const values2 =
{"c":"44", "e":"39"};
const results = [];
function addData(data) {
Object.entries(data).forEach(([k, v]) => {
let find = results.find(f => f.name === k);
if (!find) {
find = {
name: k,
data: []
}
results.push(find);
}
find.data.push(v);
});
}
addData(values1); //data packet one arrives
addData(values2); //data packet two arrives
console.log(results); //results contains both data packet one & two.
You might be able to get away with a simpler data structure like, eg. { a: [43, 42, 43], b: [1, 2, 3] }
ie. instead of having separate name and data keys, you could use name as the key, and the data array as the value.
If this would work to represent the timeline for each key, and your initial data is structured like, eg. [{ a: 43, b: 1, c: 3 }, { a: 42, b: 2, c: 3 }], then something like this might be suitable to transform the latter into the former:
const output = {};
temp_data.forEach(x => {
for (const key in x) {
const y = x[key];
if (typeof output[key] === 'undefined') {
output[key] = [];
}
output[key].push(y);
}
});
This produces an object whose keys match the keys in your data points (eg. "a", "b", "c", etc), and whose values are an array of all the values for each of these keys, which might be suitable for plotting a timeline.
(Incidentally, if you want to plot these as values on a graph, it might be better to treat the values as numbers - eg. 1, 2, 3 - rather than strings - eg. "1", "2", "3").
There are probably more elegant, functional-style ways of doing this, but this might do the job!
It seems to me that you want to be able to add multiple datasets to the data object. One approach is to have a data object with methods that know how to do things like add data to themselves, maybe something like the following. You might want to keep the index property private, and maybe sort it so it's always in a particular order regardless of the order the values are added.
var data0 = {"a":"43",
"b":"43",
"c":"42",
"d":"43"};
var data1 = {"a":"53",
"b":"53",
"c":"52",
"d":"53",
"e":"65"
};
class DataObject {
constructor (data) {
this.index = [];
this.data = [];
if (data) {
this.addData(data);
}
}
addData (data) {
Object.keys(data).forEach(key => {
let idx = this.index.indexOf(key);
if (idx == -1) {
idx = this.index.push(key) - 1;
this.data.push({name:key, data:[]});
}
this.data[idx].data.push(data[key]);
});
}
}
// Initialise object with some data
let myData = new DataObject(data0);
console.log(JSON.stringify(myData.data));
// Add more data
myData.addData(data1);
console.log(JSON.stringify(myData.data));

filter existing array of object using simple map and filter es6

Try to avoid using any library as I just need a simple script. I want to get non existing record from existing array.
input = [{name: 'james'}, {name: 'jane'}]
existing = [{name: 'james'}]
//do something
expected input to become
[{name: 'jane'}]
I tried this
let input = [{
name: 'yahoo.com',
},{
name: 'google.my',
}]
existing = (existing || []).map(o => ({name: o.name})) //clean up data from backend [{name:'google.my'}]
input = (input || []).map(o => o.name) //just in case input has more property in the future
input = existing.filter(o => !o.name.includes(input))
console.log(input)
Somehow I still don't get what I want (expect input to be [{name: 'yahoo.com'}], what is missing? I couldn't spot it.
You could filter with a lookup with find.
var input = [{ name: 'james' }, { name: 'jane' }],
existing = [{ name: 'james' }],
result = input.filter(({ name }) => !existing.find(o => o.name === name));
console.log(result);
Array.prototype.filter, Array.prototype.map, and Set can be combined using a closure to detect missing elements between arrays of objects using keys.
See below for a practical example.
// Input.
const input = [{name: 'james'}, {name: 'jane'}]
// Existing.
const existing = [{name: 'james'}]
// Missing (B elements from A).
const missing = (A, B) => (s => A.filter(({name}) => !s.has(name)))(new Set(B.map(({name}) => name)))
// Output.
const output = missing(input, existing)
// Proof.
console.log(output)
You can use filter and find
let input = [{name: 'james'}, {name: 'jane'}];
let existing = [{name: 'james'}];
let result = input.filter(v => !existing.find(o => o.name == v.name));
console.log(result);
You can use a combination of two Array#filter.
The first one is looping through your input array, while the second one loops through your existing array to check if each input value is contained inside existing.
let input = [{name: 'james'}, {name: 'jane'}];
let existing = [{name: 'james'}];
let res = input.filter(a => !existing.filter(b => b.name == a.name).length);
console.log(res);
First, don't reuse variable names, it's confusing (you have two separate things called input.
Second, don't do unnecessary loops. Do a filter right out of the gate and then map to get an array of names (or skip the map altogether if you don't really need it)
let input = [
{
name: 'yahoo.com'
},
{
name: 'google.my'
}
]
//all names in existing that are also in input
(existing || [])
//I used some instead of includes
.filter(o => !(input || []).some(i => i.name == o.name))
.map(o => o.name);
MDN Array.prototype.some

Functional Javascript map with index

I need a functional approach to solve a very basic problem, the problem with list indexes, let me write an example with React and ramda that shows the need of an index.
const R = require('ramda');
const array = ["foo", "bar", "foobar"];
// Need to put them in a nice html li ?
// this works with a warning that you
// need a unique key to each item.
const renderList = R.map( item => <li>{item}</li> );
// we can solve it like that.
const mapIndexed = R.addIndex(R.map)
const renderListIndexed = mapIndexed((item, id) => <li key={id}>{item}</li>
All of that is cool, but I'm pretty sure the use of an indexed map is not a functional approach, let me know if I'm wrong.
I'm not sure what is Ramda doing since I'm not familiar with React stuff, but if you need an index for elements of your array, you can use basic Array.map function.
const array = ["foo", "bar", "foobar"];
array.map(function(item, index) {
return {
item: item,
id: index
}
});
which will give you an array of objects structured as:
[{ id: 0, item: "foo" }, { id: 1, item: "bar" }, { id: 2, item: "foobar" }]
Hope this helps!
Look at addIndex. There are some very good reasons why Ramda does not include it by default, but this function will mix it in for you.
Try
const renderList = R.addIndex(R.map)( item => <li>{item}</li> );

Categories

Resources