Mapping an array into an object, key is not defined - arrays

const lessons = [
[ 'Chemistry', '9AM', 'Mr. Darnick' ],
[ 'Physics', '10:15AM', 'Mrs. Lithun'],
[ 'Math', '11:30AM', 'Mrs. Vitalis' ]
];
let lessonsAsObject = {};
lessons.map(lesson => {
lessonsAsObject[lesson[subject]] = lesson[0];
});
I want to translate this array into an object, matching the data with the keys, subject, time and teacher.
However the code above returns
reference error, "subject is not defined".

You can use array destructuring to get the params from the array, and then create an object for each of them, using the shorthand and computed property names.
If you want the end result to be an object, instead of array of objects, you can combine them to one object using spread syntax, and Object#assign:
You can use the subject as key:
const lessons = [["Chemistry","9AM","Mr. Darnick"],["Physics","10:15AM","Mrs. Lithun"],["Math","11:30AM","Mrs. Vitalis"]];
const lessonsAsObject = Object.assign(...lessons.map(([subject, time, teacher])=> ({
[subject]: {
time,
teacher
}
})));
console.log(lessonsAsObject);
Or the index as key:
const lessons = [["Chemistry","9AM","Mr. Darnick"],["Physics","10:15AM","Mrs. Lithun"],["Math","11:30AM","Mrs. Vitalis"]];
const lessonsAsObject = Object.assign(...lessons.map(([subject, time, teacher], index)=> ({
[index]: {
subject,
time,
teacher
}
})));
console.log(lessonsAsObject);
If you just want an array of objects:
const lessons = [["Chemistry","9AM","Mr. Darnick"],["Physics","10:15AM","Mrs. Lithun"],["Math","11:30AM","Mrs. Vitalis"]];
const lessonsAsObject = lessons.map(([subject, time, teacher])=> ({ subject, time, teacher }));
console.log(lessonsAsObject);

If what you're trying to end up with is an array of objects where the objects have named properties instead of your array positions, then you could do this:
var lessonsList = lessons.map(function(lesson) {
return {subject: lesson[0], time: lesson[1], teacher: lesson[2]};
});
This would give you an array of objects like this:
[{subject: "Chemistry", time: "9AM", teacher: "Mr. Darnick"},
{subject: "Physics", time: "10:15AM", teacher: "Mrs. Lithun"},
{subject: "Math", time: "11:30AM", teacher: "Mrs. Vitalis"}]
If you're looking for some different type of output, then please show an example of exactly what output you're trying to achieve.
However the code above returns the reference error, "subject is not
defined".
That is because the identifier subject is not defined anywhere. If you intended to use it as a static property name, then you can do lesson["subject"]. If you intended to have subject be a variable with some property name in it, then you have to define and assign that variable.

Related

Generic Typescript function to check for duplicates in an array

I'm trying make a generic Typescript function to check if an array contains duplicates. For example:
interface Student {
name: string;
class: string;
};
const students: Student[] = [
{ name: 'John Smith', class: 'Science' },
{ name: 'Edward Ryan', class: 'Math' },
{ name: 'Jessica Li', class: 'Social Studies'},
{ name: 'John Smith', class: 'English'}
];
That is the data.
This is what I want to do with the data:
const registerStudents = async (students: Student[]): Promise<void> {
checkDuplicate(students, existingState); //This is the function I want to build
const response = await axios.post('/students/new', students)
existingState.push(response); //pushes newly registers students to the existing state
};
Regarding the checkDuplicate(), I want to make it a generic function, but I'm struggling with the logic.
export const checkDuplicate = <T>(items: T[], existingState: T[]): void {
//checks if the items have any duplicate names, in this case, it would be 'John Smith', and if so, throw an error
//Also checks if items have any duplicate names with the existingState of the application, and if so, throw an error
if (duplicate) {
throw new Error('contains identical information')
};
};
It's a little bit complex and I haven't been able to figure out the logic to work with typescript. Any advice on how I can implement this would be appreciated!
One reasonable way to approach this is to have checkDuplicate() take a single array items of generic type T[], and another array keysToCheck of type K[], where K is a keylike type (or union of keylike types) and where T is a type with keys in K and whose values at those keys are strings. That is, the call signature of checkDuplicate() should be
declare const checkDuplicate: <T extends Record<K, string>, K extends PropertyKey>(
items: T[],
keysToCheck: K[]
) => void;
This function should iterate over both items and keysToCheck, and if it finds an item where a property is the same string as the same property in a previous item, it should throw an error.
If you had such a function, you could write the version which accepts students and existingState, two arrays of Student objects, like this:
function checkDuplicateStudents(students: Student[], existingState: Student[]) {
checkDuplicate([...students, ...existingState], ["name", "class"]);
}
where we are spreading the students and existingState arrays into a single array to pass as items to checkDuplicate(), and since we are checking Student we are passing ["name", "class"] as keysToCheck.
Here's a possible implementation of checkDuplicate():
const checkDuplicate = <T extends Record<K, string>, K extends PropertyKey>(
items: T[],
keysToCheck: K[]
): void => {
const vals = {} as Record<K, Set<string>>;
keysToCheck.forEach(key => vals[key] = new Set());
for (let item of items) {
for (let key of keysToCheck) {
const val: string = item[key];
const valSet: Set<string> = vals[key]
if (valSet.has(val)) {
throw new Error(
'contains identical information at key "' +
key + '" with value "' + val + '"');
};
valSet.add(val);
}
}
}
The way it works is that we create an object named vals with one key for each element key of keysToCheck. Each element vals[key] is the Set of strings we have already seen for that key. Every time we see a string-valued property val with key key in any item in the items array, we check whether the set in vals[key] has val. If so, we've seen this value for this key before, so we throw an error. If not, we add it to the set.
(Note that it's possible to replace Set<string> with a plain object of the form Record<string, true | undefined>, as shown in Mimicking sets in JavaScript? , but I'm using Set here for clarity.)
Okay, let's test it against your example:
checkDuplicateStudents(students, []);
// contains identical information at key "name" with value "John Smith"
Looks good. It throws an error at runtime and properly identifies the duplicate data.
Playground link to code

Collect unique values of array in an array React

Can someone let me know how I can create a list of unique languages from an array inside another array.
This is the dataset...
const people = [
{
//Other values, name date etc.
languages: ["English", "Spanish"]
},{
//Other values, name date etc.
languages: ["English", "Mandarlin"]
},{
//Other values, name date etc.
languages: ["Japanese"]
},....
and here is as far as I've got....
const languagesOptions = this.props.data.map((item, index) => {
new Map(
...item.languages.map(d => [d.languages])
)
});
I can use the new Map function when it's not an array but can't get it to work with the languages data.
Thanks
You could map over it and use Set to remove dupes.
const people = [{
languages: ["English", "Spanish"]
}, {
languages: ["English", "Mandarlin"]
}, {
languages: ["Japanese"]
}];
const languages = [...new Set(people.flatMap(({ languages }) => languages))];
console.log(languages);

Flatten object type with array property in typescript react for output to react-data-grid

Apologies in advance if this has already been answered, but I am struggling to find an answer to it...
I have an array which contains a 2 d array property.
What I want returned is a type which contains has the inner array as a flat object.
So for example my array could be
{
name: "Widget",
event: "Xmas",
pilot: "Dave",
session: "drinking",
frameType: "flight",
stint: 2016,
plane: "737",
**data: {
"114": "137.623",
"115": "51.090",
}**
}
What I would like is my output to be
{
name: "Widget",
event: "Xmas",
pilot: "Dave",
session: "drinking",
frameType: "flight",
stint: 2016,
plane: "737",
"114": "137.623",
"115": "51.090",
,
}
Now here is my code to generate the array.
The Type:
type TableItem =
{
name: string,
event: string,
session: string,
frameType: string,
stint: number,
chassis: string,
driver: string,
data: (string | number)[][]
};
const getTableItem = (index: number) =>
{
const d = data[index];
//Transformentry just makes the id 3 digits
const dataItems = Object.assign({}, ...Object.entries(d.data).map(transformEntry));
const tableItem: TableItem = {
name: d.name,
event: d.event,
piolt: d.pilot,
session: d.session,
frameType: d.frameType,
stint: d.stint,
plane: d.plane,
data: dataItems
};
return tableItem;
};
const rows = (data.map((d, index) => { return getTableItem(index); }));
Now what I want is the rows variable(const) to contain the flattened array. I have tried flat/flatmap and reduce but can't get them to work.
If anyone can point me in the right direction with this it would be massively appreciated. Basically the rows const will then be passed to the react-data-grid component.
Thanks in advance
Steve
The data property is not an array, it is another object which may be why things like flatMap did not work for you.
If you're happy to retain the data property but also flatten the properties therein into the top level object you could just flatten it with the spread operator ...:
const input = {
name: "Widget",
event: "Xmas",
pilot: "Dave",
session: "drinking",
frameType: "flight",
stint: 2016,
plane: "737",
data: {
"114": "137.623",
"115": "51.090",
}
};
const result = {...input,...input.data};
console.log(result);
If you must get rid of the data property you could just add delete result.data; to the above.

How to convert an array of dictionaries into an array of keys using react

Given a list:
let names = [{name: "bobby"}, {name: "sydney"}, {name: "Paul"}, {name: "Grace"}
I want the output to be ["bobby", "sydney", "Paul", "Grace"]
Here is what I have tried:
var items = Object.keys(names).map(function(i) {
return names[i];
})
const items = Object.keys(names).map((key)=>names[key]);
this.setState({items});
console.log(this.state.items);
names.map(({ name }) => name)
const names = [{
name: "bobby"
}, {
name: "sydney"
}, {
name: "Paul"
}, {
name: "Grace"
}];
const keys = names.map(({
name
}) => name);
console.log(keys);
A note about react keys, they should be unique within the rendered siblings, i.e. they should be unique within the dataset. Names alone may not provide sufficient uniqueness.
A second note, you might not want to generate your react keys separately from where you need them, i.e. generally they are created when you are mapping JSX.
This is not really related to React. You can do that with JavaScript, for instance using API like map().
Here is an example:
let arr = names.map(obj => obj.name);

Immutable JS - getIn() returns undefined but works in console from the browser

Here is a snippet from my code:
this.state = {
data: Immutable.Map({
gender: "",
provinces: Immutable.Map(),
activeProvince: this.props.defaultProvince,
activeCity: this.props.defaultCity,
})
}
let cities = this.state.data.getIn(['provinces', '354']);
The structure would look like this:
state{
gender: "",
activeProvince: "354",
provinces: {
354: {
name: "abc",
cities: Immutable.Map()
},
123: {
name: "def",
cities: Immutable.Map()
}
}
getting the 'provinces' only returns a Map with a size of 87 which means it has value, but going 1 level deeper gives me an undefined. Doing it in the console from the browser gives me the expected result.
I am doing it from the render() right before the "return"
render(){
let provinces = this.state.data.get('provinces')
.sortBy( province => province.get('name') )
.map( (value, key) => {
return { text: value.get('name'), value: key }
});
// Supposed to do the same thing as above (provinces)
// with ['provinces'] only it returns a value, adding '354' returns undefined.
let cities = this.state.data.getIn(['provinces', '354']);
console.log(cities);
-- EDIT --
converting the provinces Map to an object by doing 'toObject()' shows the key exists, so why does getIn('provinces,'354') shows undefined?
When I try to reproduce your problem, I notice that provinces key and its one level children are immutable, but name and cities keys are actually mutable. That's why, I'm getting error on the line containing sortByfunction. To solve this, you need to define provinces with Immutable.fromJS() instead of Immutable.Map(). Immutable.Map() converts object to immutable for only one level. However, Immutable.fromJS(), converts all levels to immutable.

Resources