How do I sort some HTML elements alphabetically? - javascript

function render() {
let lastContact = listadeContatos.slice(-1);
boxapp__contact.innerHTML += lastContact
.map((contact) => {
return `<div class="box-app__contacts-list" id=${
contact.id
} onclick="boxapp(this.id)">
<div class="contact__nameinitial">${contact.fistName[0].toUpperCase()}</div>
<div class="contact__name">${contact.fistName}</div>
</div>`;
}).join("");
const contacts = document.querySelector(".box-app__contacts-list");
const c = [...document.querySelectorAll(".contact__name")];
if (listadeContatos.length > 1) {
c.sort((a, b) => (a.innerText > b.innerText ? 1 : -1)).forEach((node) => {
contacts.appendChild(node);
});
}
}
I made a simple app, a list of contacts, and I wanted them to be sorted alphabetically when a new
contact is added and called for rendering, but nothing I've tried has worked. And the similar issues I encountered were not enough for me to find the way to a solution. I'm still learning Javascript. I would like someone to help me if they can. And sorry if there are any spelling mistakes. I'm using Google Translate to make this post.

Try using this sort function: c.sort((a, b) => (a.innerText < b.innerText? -1 : a.innerText > b.innerText? 1 : 0))

Have a function that sorts array of objects with .sort() and .localeCompare() first, then render the HTML with a seperate function. If the data is changed in any way, you use those two functions the same way every time. This pattern is less error prone as long as the data doesn't vary in keys or pattern.
Details are commented in example
const contactData = [{
firstName: "Brandon",
lastName: "Grewe",
phone: ["205-116-8677", "961-849-8402", "112-125-1509"],
email: ["sgrewe0#rediff.com"]
}, {
firstName: "Lyndsay",
lastName: "Courtes",
phone: ["559-585-5161", "244-413-5832", "467-871-0638"],
email: ["lcourtes1#sun.com"]
}, {
firstName: "Rose",
lastName: "Xavier",
phone: ["293-858-6159"],
email: ["gpopley2#uol.com.br", "gpopley2#npr.org"]
}, {
firstName: "Berni",
lastName: "Molen",
phone: ["944-271-6491", "911-107-2055", "429-553-3494"],
email: ["bmolen3#gnu.org"]
}, {
firstName: "Rose",
lastName: "Fereday",
phone: ["820-272-1567"],
email: ["rfereday4#naver.com"]
}, {
firstName: "Juliana",
lastName: "Fredi",
phone: ["268-283-9358", "981-219-0701", "326-164-3537"],
email: ["jfredi5#bigcartel.com", "jfredi5#exblog.jp"]
}, {
firstName: "Barry",
lastName: "Gatchel",
phone: ["762-132-3931", "840-627-2075", "654-204-6164"],
email: ["mgatchel6#com.com"]
}, {
firstName: "Jeth",
lastName: "Harwin",
phone: ["871-777-0358", "968-282-0040"],
email: ["jharwin7#yahoo.co.jp", "jharwin7#icq.com"]
}, {
firstName: "Ahmad",
lastName: "Scoon",
phone: ["392-928-5655"],
email: ["ascoon8#ask.com", "ascoon8#nyu.edu"]
}, {
firstName: "Kristine",
lastName: "Sleet",
phone: ["104-606-6882"],
email: ["ksleet9#fc2.com", "ksleet9#php.net"]
}];
// Pass in an array of objects
function arrange(array) {
// Returns an object of arrays -- each keyed to a letter A-Z
let letters = Object.fromEntries(
[...new Array(26)]
.map((_, i) => i + 65)
.map(n => [String.fromCharCode(n), []])
);
//console.log(letters);
/*
Takes the given array and gets the first letter of each firstName
property then finds the array with that letter inside the letters
object and adds the current object to it's array -- once in
the array, it is sorted alphabetically by firstName or
lastName should firstName be identical
*/
array.forEach(con => {
let alpha = con.firstName.charAt(0);
letters[alpha].push(con)
letters[alpha].sort(
(a, b) => a.firstName.localeCompare(b.firstName) == 0 ?
a.lastName.localeCompare(b.lastName) :
a.firstName.localeCompare(b.firstName)
)
});
//console.log(letters);
// Filters out any letters that have empty arrays
letters = Object.fromEntries(Object.entries(letters)
.filter(sub => sub[1].length > 0)
);
//console.log(letters);
return letters;
}
// Pass in the object returned from arrange() and an optional DOM object
function build(object, node = document.body) {
/*
For each key of object render a <fieldset> and add the letter
to it's <legend> and it's #id
*/
Object.keys(object).forEach(letter => {
const alphabet = `
<fieldset id='${letter}' class='letter'>
<legend>${letter}</legend>
</fieldset>`;
node.insertAdjacentHTML('beforeend', alphabet);
});
// Make an array of the <fieldset>s
const letters = [...document.querySelectorAll('.letter')];
/*
For each <fieldset> find the array of the object that
corresponds to the <fieldset>'s #id. For each object of the
sub-array interpolate the object's data within the template
literals, then render the template literal to the current <fieldset>
*/
letters.forEach(az => {
let group = object[az.id].map(contact => {
const fullName = `
<fieldset class='contact'>
<legend>
${contact.firstName}
${contact.lastName}
</legend>
<menu>`;
const numbers = contact.phone.map(tel => `
<li>Phone:
<output name='phone'>${tel}</output>
</li>`
).join('');
const address = contact.email.map(pop => `
<li>Email:
<output name='email'>${pop}</output>
</li>`
).join('');
return `${fullName}${numbers}
</menu><menu>${address}</menu>
</fieldset>`;
});
az.insertAdjacentHTML('beforeend', group.join(''));
});
}
const data = arrange(contactData);
build(data);

Related

Define custom parameters inside javascript compare functions in JavaScript?

I have an array of objects that needs sorting by last name, however the object only has firstname and fullname parameters, so for last name I have to use fullname.replace((firstname + " "), ""). Example array below.
const names = [
{
firstname: "John",
fullname: "John Doe"
},
{
firstname: "Amber",
fullname: "Amber Smith"
},
{
firstname: "Michael",
fullname: "Michael Smith"
},
{
firstname: "Jessica",
fullname: "Jessica Kelly Charles"
}
]
While I can use the "replace" every time inside a sort compare function, I'd much rather have something like this:
names.sort(function(a, b) {
const lastname = fullname.replace((firstname+ " "), "");
if (a.lastname < b.lastname) {
return -1;
} else if (a.lastname > b.lastname) {
return 1;
}
return 0;
});
Obviously lastname comes up as undefined.
This has been rather hard to google for, and I think I'm missing some JavaScript basics here, but would greatly apprecite your help in helping me learn to write better code.
Your best bet is to modify the source of the array so it stores lastname upon collection.
If you can't do that:
Unless you do a prep pass through your array adding a lastname property, you'll have to compute it each and every time your sort callback is called, for both a and b.
names.sort((a, b) => {
const alast = a.fullname.replace(a.firstname + " "), "");
const blast = b.fullname.replace(b.firstname + " "), "");
return alast.localeCompare(blast);
});
(Note I used localeCompare, which is always a better choice for names and other natural language strings than < and >. For instance, ask the French whether รง should really come after z as it does with < and >. ;-) )
That will recompute the lastname for the same object repeatedly, though, since the same object may be passed to sort (as either a or b) repeatedly. If you think that may be a problem, you could do that prep pass I mentioned:
// Build a map of entry to last names
const lastnames = new Map(names.map(entry => {
const lastname = entry.fullname.replace(entry.firstname + " ", "");
return [entry, lastname];
}));
// sort
names.sort((a, b) => {
return lastnames.get(a).localeCompare(lastnames.get(b));
});
You are just declaring a new variable trying to access it via object's property. You should map your data in the first place like:
const names = [
{
firstname: "John",
fullname: "John Doe"
},
{
firstname: "Amber",
fullname: "Amber Smith"
},
{
firstname: "Michael",
fullname: "Michael Smith"
},
{
firstname: "Jessica",
fullname: "Jessica Kelly Charles"
}
].map(person => ({
...person,
lastName: person.fullName.replace(person.firstName + ' ', '')
}));
After mapping your data like that, you can use .lastName in the place you need.
names.sort(function(a, b) {
if (a.lastName < b.lastName) {
return -1;
} else if (a.lastName > b.lastName) {
return 1;
}
return 0;
});

Map specific properties from an array into a new array

I have the following array of employees. I want to create a new array employeeNames that has only the firstName and lastName properties. I believe I can do this with the map() method but I'm not exactly sure how.
let employees = { firstName: 'Collin', lastName: 'Sexton', email: 'exampe#test.com', dob: '03/20/1986' ... }
Resulting array should look like:
employeeNames = { firstName: 'Collin', lastName: 'Sexton' }
You are not looking for array as a result though.. do this
let { firstName, lastName } = employees;
let employeeNames = { firstName, lastName };
What we do here is called deconstruction. We bind the values of 'firstName' and 'lastName' to variables with the same name and then create a new Object with those variables. If no keys are given, it automagically names them after the variables.
Probably you meant you have array of objects. So you'd do:
const projected = empArray.map((empItem) => ({
firstName: empItem.firstName,
lastName: empItem.lastName,
}));
employees is an object which is not iterable, so you need to convert before being able to map() a new one.
Which can easity be made with Object.entries()
so..
// key is the object key while value is the object value of corresponding key
let newArray = Object.entries(employees).map((key, value) => {
let arrayEntry;
if (key === 'firstName' || key === 'lastName') {
newEntry = value;
}
return arrayEntry;
});
Your new array should then output to [John, Doe, Jane, Doe, name, lastname]
Here is how it will be if you need to map array of eployees to array with only needed props:
let employees = [{ firstName: 'Collin', lastName: 'Sexton', email: 'exampe#test.com', dob: '03/20/1986'}]
let result = employees.map({firstName, lastName}=> ({ firstName, lastName}));

map nested array into parent object's key value pairs

I have an array of objects that looks like:
{
name: 'steve',
plaintiff:'IRS'
amount: 5000,
otherliens:[
{
amount:5000,
plaintiff:'irs'
},
{amount:5000,
plaintiff:'irs'
}
]
}
i need to send this as a csv so i need to map and iterate over this subarray and flatten it into them ain object like so:
{
name:'steve',
plaintiff:'irs',
amount:5000,
plaintiff2:'irs',
amount2:5000,
plaintiff3:'irs',
amount3:5000
}
the code i use to normally do this process is by mapping the contents of the original array into a new array with arr.map(a,i =>{ a[i] ? a[i].amount = a[i].amount }) i am able to work the the subarrays that are string based by flat guessing a number of entries (see phones and emails) because if i return null it just returns blank, which in the csv isnt the worst thing. but i cannot do the same because accessing a sub property of an element that doesnt exist obviously wont work. So here is the map im using where emailAddresses is a string array, phoneNumbers is a string array and otherliens is an object array.
any help would be appreciated and bear in mind because it is bulk data transfer and csvs that will have a fixed number of columns in the end, i dont mind null values, so i guess you would take the longest subarray length and use that in all the other objects.
Full code
prospects.map((list, i) => {
result[i]
? (result[i].fullName = list.fullName)
(result[i].First_Name = list.firstName)
(result[i].Last_Name = list.lastName)
(result[i].Delivery_Address = list.deliveryAddress)
(result[i].City = list.city)
(result[i].State = list.state)
(result[i].Zip_4 = list.zip4)
(result[i].County = list.county)
(result[i].plaintiff= list.plaintiff)
(result[i].Amount = list.amount)
(result[i].age = list.age)
(result[i].dob= list.dob)
(result[i].snn= list.ssn)
(result[i].plaintiff2= list.otherliens[1].plaintiff )
(result[i].filingDate2= list.otherliens[1].filingDate)
(result[i].amount2= list.otherliens[1].amount )
(result[i].plaintiff3= list.otherliens[2].plaintiff)
(result[i].filingDate3= list.otherliens[2].filingDate )
(result[i].amount3= list.otherliens[2].amount )
(result[i].amount4= list.otherliens[3].amount)
(result[i].plaintiff4= list.otherliens[3].plaintiff )
(result[i].filingDate4= list.otherliens[3].filingDate )
(result[i].phone1 = list.phones[0])
(result[i].phone2 = list.phones[1])
(result[i].phone3 = list.phones[2])
(result[i].phone4 = list.phones[3])
(result[i].phone5 = list.phones[4])
(result[i].phone6 = list.phones[5])
(result[i].phone7 = list.phones[6])
(result[i].phone8 = list.phones[7])
(result[i].phone9 = list.phones[8])
(result[i].emailAddress1 = list.emailAddresses[0])
(result[i].emailAddress2 = list.emailAddresses[1])
(result[i].emailAddress3 = list.emailAddresses[2])
(result[i].emailAddress4 = list.emailAddresses[3])
(result[i].emailAddress5 = list.emailAddresses[4])
(result[i].emailAddress6 = list.emailAddresses[5])
(result[i].emailAddress7 = list.emailAddresses[6])
: (result[i] = {
Full_Name: list.fullName ,
First_Name: list.firstName,
Last_Name: list.lastName,
Delivery_Address: list.deliveryAddress,
City: list.city,
State: list.state,
Zip_4: list.zip4,
County: list.county,
dob: list.dob,
ssn:list.ssn,
age:list.age,
Amount: list.amount,
plaintiff: list.plaintiff,
filingDate: list.filingDate,
phone1:list.phones[0],
phone2:list.phones[1],
phone3:list.phones[3],
phone4:list.phones[4],
phone5:list.phones[5],
phone6:list.phones[6],
phone7:list.phones[7],
phone8:list.phones[8],
emailAddress1:list.emailAddresses[0],
emailAddress2:list.emailAddresses[1],
emailAddress3:list.emailAddresses[2],
emailAddress4:list.emailAddresses[3],
emailAddress5:list.emailAddresses[4],
emailAddress6:list.emailAddresses[5],
plaintiff2: list.otherliens[1].plaintiff,
amount2: list.otherliens[1].amount,
filingDate2: list.otherliens[1].filingDate,
plaintiff3: list.otherliens[2].plaintiff,
filingDate3: list.otherliens[2].filingDate,
amount3: list.otherliens[2].amount,
plaintiff4: list.otherliens[3].plaintiff,
amount4: list.otherliens[3].amount,
filingDate4: list.otherliens[3].filingDate,
})
} );
Use loops to assign properties from the nested arrays, rather than hard-coding the number of items.
I also don't see the need for the conditional expression. Since each input element maps directly to an output element, there won't already be result[i] that needs to be updated.
result = prospects.map(({fullName, firstName, lastName, deliveryAddress, city, state,zip4, county, plaintiff, amount, age, dob, ssn, otherliens, phones, emailAddresses}) => {
let obj = {
fullName: fullName,
First_Name: firstName,
Last_Name: lastName,
Delivery_Address: deliveryAddress,
City: city,
State: state,
Zip_4: zip4,
County: county,
plaintiff: plaintiff,
Amount: amount,
age: age,
dob: dob,
ssn: ssn
};
otherliens.forEach(({plaintiff, amount}, i) => {
obj[`plaintiff${i+2}`] = plaintiff;
obj[`amount${i+1}`] = amount;
});
phones.forEach((phone, i) => obj[`phone${i+1}`] = phone);
emailAddresses.forEach((addr, i) => obj[`emailAddress${i+1}`] = addr);
return obj;
})

How to remove an element from an array in a nested object? (lodash) [duplicate]

This question already has answers here:
How can I remove an element from a list, with lodash?
(10 answers)
Closed 3 years ago.
I had an object like
data: {
registrationNumber: 'MH1234',
type: 'xyz',
driver: {
user: {
firstName: 'xyz',
lastName: 'abc',
email: 'xyz',
phone: 1234,
city: 'random',
dateOfBirth: new Date(),
groups: [1]
}
},
owner: {
user: {
firstName: 'xyz',
lastName: 'abc',
email: 'xyz',
phone: '1234',
city: 'random',
groups: [1, 2]
},
kyc: [
{
method: 'xyz',
id: 'abcd'
},
{
method: 'xyz',
id: 'abcd'
}
]
}
}
how can i remove the element at path data.owner.kyc.0 ?
I have tried using _.unset() but it is not ideal since the array still shows 2 elements after deletion
You don't need lodash to accomplish this.
With .shift() to remove first element from array.
let newData = {...data}
newData.owner.kyc.shift()
With .splice() to remove elements from a certain point.
let newData = {...data}
newData.owner.kyc.splice(0, 1)
With .filter() to remove items that don't match a specified condition
let newData = {...data}
newData.owner.kyc = newData.owner.kyc.filter((item, index) => {
return index !== 0
})
Since you're using React, you would want to have this data in component-state so that when you actually remove something from the array, the App updates to reflect the new results.
See sandbox: https://codesandbox.io/s/mystifying-fog-2yhwd
class App extends React.Component {
state = {
data: data
};
removeFirstItem = () => {
const { data } = this.state;
let newData = { ...data };
newData.owner.kyc.shift();
this.setState({
data: newData
});
};
render() {
const { data } = this.state;
return (
<div>
<h4>Car Info:</h4>
<div>
KYC:
{data.owner.kyc.map((item, index) => {
return (
<div>
<span>{index + 1}) </span>
{item.id}
</div>
);
})}
</div>
<button onClick={this.removeFirstItem}>Remove first item</button>
</div>
);
}
}
You can use _.remove or _.filter.
Please refer this example
You can use a Array prototype method itself,
if you want to remove from the front
Array.prototype.shift.apply(data.owner.kyc,null)

compare keys of two objects and construct a third one with same keys to reflect the difference

I am trying to compare two objects and construct a third one if the value stored in a property of one is different than the other. If not I am storing an empty string. The way am doing it in my opinion is super inefficient and non sustainable. This wont work if the keys change or their number increases. I have to be able to do this programmatically with pure JS, no jQuery:
var staleProfile = {
firstName: 'john',
lastName: 'smith',
email: 'js#gmail.com'
}
var CurrentCustomer = {};
CurrentCustomer.profile = {
firstName: 'paul',
lastName: 'smith',
email: 'js#yahoo.com'
}
var returnObj = {};
returnObj.firstName = staleProfile.firstName != CurrentCustomer.profile.firstName ? CurrentCustomer.profile.firstName : ''
returnObj.lastName = staleProfile.lastName != CurrentCustomer.profile.lastName ? CurrentCustomer.profile.lastName : ''
returnObj.email = staleProfile.email != CurrentCustomer.profile.email ? CurrentCustomer.profile.email : ''
This searches for all the object keys in the CurrentCustomer.profile and checks for differences to the staleProfile. It adds keys to the diff, that only exist in object1 but not in object0, but doesn't take care of keys that are removed from object0to object1.
var staleProfile = {
firstName: 'john',
lastName: 'smith',
email: 'js#gmail.com',
key0: 'value0'
}
var CurrentCustomer = {};
CurrentCustomer.profile = {
firstName: 'paul',
lastName: 'smith',
email: 'js#yahoo.com',
key1: 'value1'
}
function createDiffObject(object0, object1) {
var diff = {};
Object.keys(object1).forEach(function(key) {
if (!object0.hasOwnProperty(key) || object0[key] != object1[key]) {
diff[key] = object1[key];
} else {
diff[key] = '';
}
});
return diff;
}
console.log(createDiffObject(staleProfile, CurrentCustomer.profile));
// output:
/*
{ firstName: 'paul',
lastName: '',
email: 'js#yahoo.com',
key1: 'value1' }
*/
An option would be to get all the keys present in your object using Object.keys(staleProfile).
After this you can loop through the keys and compare values. See an example down below.
var keys = Object.keys(staleProfile),
i, j,
comparedObject = {},
key,
CurrentCustomer = {
a:a,
b:b,
c:c
};
for (i = 0, j = keys.length; i < j; i++) {
key = keys[i];
if (staleProfile[key] === CurrentCustomer[key]) {
comparedObject[key] === CurrentCustomer[key];
} else {
comparedObject[key] === '';
}
}
One way to do it is to make a small helper function to assign each property (or empty string):
// NOTE: staleProfile and CurrentCustomer are assumed to be in the scope above
// If this could be an issue, just pass them as parameters
function getProp(prop) {
let staleProp = staleProfile[prop];
let currProp = CurrentCustomer.profile[prop];
return staleProp != currProp ? currProp : '';
}
And then use it like:
let returnObj = {
firstName : getProp('firstName'),
lastName : getProp('lastName'),
email : getProp('email')
}
Running Example
var staleProfile = {
firstName: 'john',
lastName: 'smith',
email: 'js#gmail.com'
}
var CurrentCustomer = {};
CurrentCustomer.profile = {
firstName: 'paul',
lastName: 'smith',
email: 'js#yahoo.com'
}
function getProp(prop) {
let staleProp = staleProfile[prop];
let currProp = CurrentCustomer.profile[prop];
return staleProp != currProp ? currProp : '';
}
let returnObj = {
firstName: getProp('firstName'),
lastName: getProp('lastName'),
email: getProp('email')
}
console.log(returnObj);
The advantage of this helper function is that, in case you need to update your strategy for replacement of a missing value, you only have to do it in one place.
Furthermore, you could have an array of keys you would like to extract:
let keys = ['firstName', 'lastName', 'email'];
Or perhaps extract all using Object.keys:
let keys = Object.keys(staleProfile);
And then you can store the keys using the getProp function:
keys.forEach(k => {
returnObj[k] = getProp(k);
});
This would make adding/removing properties simply a matter of the current content the keys array. When you need to remove or add a property, you can either update the keys array in the first example, or update the staleProfile in the second example and let Object.keys return the values for you. Either way, with one small change you can add/remove properties.
Running example:
var staleProfile = {
firstName: 'john',
lastName: 'smith',
email: 'js#gmail.com'
}
var CurrentCustomer = {};
CurrentCustomer.profile = {
firstName: 'paul',
lastName: 'smith',
email: 'js#yahoo.com'
}
function getProp(prop) {
let staleProp = staleProfile[prop];
let currProp = CurrentCustomer.profile[prop];
return staleProp != currProp ? currProp : '';
}
let keys = Object.keys(staleProfile);
let returnObj = {};
keys.forEach(k => {
returnObj[k] = getProp(k);
});
console.log(returnObj);
Note: James Thorpe made a good observation in the comment.
what if one of the future properties you speak of could validly hold an empty string? In that case, you won't know if it's been updated to a an empty string or is unchanged. You may want to consider using null for unchanged items

Categories