Angular 2 / Typescript - how to check an array of objects to see if a property has the same value? - angularjs

This question does it in Javascript, but I would have thought in Typescript I could do some kind of map/filter operation to do the same thing.
I have an array of objects called Room. Each Room has a property called Width (which is actually a string, eg '4m', '5m', '6.5m').
I need to check the entire array to see if all the widths are the same.
Based on that question I have this, but I was wondering if TypeScript has something better:
let areWidthsTheSame = true;
this.qp.rooms.forEach(function(room, index, rooms) {
if (rooms[index] != rooms[index+1]) areWidthsTheSame = false;
});
Any ideas?
FYI the linked question has a comment that links to these performance tests, which are interesting in the context of this question:

This can be done in the following way:
const widthArr = rooms.map(r => r.width);
const isSameWidth = widthArr.length === 0 ? true :
widthArr.every(val => val === widthArr[0]);
We first convert the rooms array to an array of widths and then we check if all values in widths arrays are equal.

Related

How to update an array of arrays by replacing an array item with the new corresponding array item? Reactjs

Apologies for the poorly worded title, I'm not sure how to phrase the question. I have made a quiz app in React which allows the user to choose the number of questions and for each question, there are five possible answers they can toggle between.
Because of the toggle feature, if they toggle between the possible answers, I need to be able to add the new array and replace the old corresponding one, which will then be checked to see if they are correct when they check the score. I've tried for hours to figure this out but I just can't. I've come sort of close but it only works if the user doesn't toggle between the answers.
Here's my code so far and the only semi-workable solution that I've come up with but is ugly and only works if the user doesn't toggle between answers for the same question.
function getDataFromQuestionComponent(dataFromComponent){
getQuizData.push(dataFromComponent)
// stop the score data array from growing continously
if (getQuizData.length > amount) {
maintainScoreArrayLength(getQuizData)
}
}
function maintainScoreArrayLength(quizDataArray){
// to find if which answers are matching so the original can be replaced
// with the new answer (too obscure - find a better solution)
let lastItemZeroIndexText = (quizDataArray[quizDataArray.length - 1][0].body)
for (let i=0; i<quizDataArray.length; i++) {
if (lastItemZeroIndexText === getQuizData[i][0].body) {
newArray.push(getQuizData.indexOf(getQuizData[i]))
}
}
// remove the previous item from the array
getQuizData.splice(newArray[0], 1)
}
This is what the array of object arrays looks like in the console:
The .map array method and array destructuring make it possible to do this more cleanly.
I can't quite tell how you are storing the data for the quiz questions/answers, but here's an example of how you could do it if you are saving all of the quiz data in a state object and storing the index of the current question:
const [quizData, setQuizData] = useState(initialQuizData);
const [currentQuestion, setCurrentQuestion] = useState(0);
const onSelectAnswer = (selectedAnswer) => {
// create an object with updated data for the current question
const updatedQuestionData = {
...quizData[currentQuestion],
multipleChoiceAnswers: quizData[
currentQuestion
].multipleChoiceAnswers.map((answerOption) => ({
...answerOption,
selected: selectedAnswer === answerOption.body
}))
};
// iterate through the questions
// and save either the
// updatedQuestionData or the prevState data
// (store the updatedQuestionData over the prevState data for that question)
setQuizData((prevState) => [
...prevState.map((question, index) =>
currentQuestion === index ? updatedQuestionData : question
)
]);
};
To break it down, we create a copy of the answers array but we set selected to false unless it is the answer the user just clicked:
multipleChoiceAnswers.map(answerOption) => ({
...answerOption,
selected: selectedAnswer === answerOption.body
})
The updated question object is made by copying the data from the current question, then overriding the array of answers with the updated copy of the answers we just made in the code snippet above. That is what is happening in this part:
const updatedQuestionData = {
...quizData[currentQuestion],
multipleChoiceAnswers: // The updated answers array
};
Here's a full example on code sandbox:

Adding an Array to an Array in Angular

So I have a db document that holds some string values in an array, I want to push just the array from every entry into an array in the application for usage later, But I can see the array fine on the fetch, and when I iterate it but my "Global array" is staying empty can someone explain why?
specialDates : Specialdates[] = [];
specialRange: any[] = [];
this.specialDates.forEach(ag => {
//ag,range -> I can see fine
this.specialRange.push(ag.range);
//this.specialrange -> Stays empty
});
Array looks something like the following:
1205,1206,1207,1208
What is wrong with this approach?
Reason for doing this is because the documents have 2 fields minimum: EG ->
ID/Array
And I just need the array
this.specialRange = this.specialDates.map(ag => ag.range)

Find objects that include an array that contains all elements of a second array

I'm trying to filter a set of objects based on values in one of their elements based on another array. I've got it working with filter just fine if the search is "OR" - it returns give me all objects for which at least one of the strings in the search array is found.
But I can't figure out how to make it work as an AND search - returning only the objects that match ALL of the strings in the search array.
Example:
struct Schedule {
let title: String
let classTypes: [String]
}
let schedule1 = Schedule(title: "One", classTypes: ["math","english","chemistry","drama"])
let schedule2 = Schedule(title: "Two", classTypes: ["pe","math","biology"])
let schedule3 = Schedule(title: "Three", classTypes: ["english","history","math","art"])
let schedules = [schedule1, schedule2, schedule3]
let searchArray = ["math", "english"]
//works for OR - "math" or "english"
var filteredSchedules = schedules.filter { $0.classTypes.contains(where: { searchArray.contains($0) }) }
I'd like to find a way for it to use the same search array
let searchArray = ["math", "english"]
But only return items 1 & 3 - as they both have BOTH math and english in the list.
There are good examples of AND conditions when the AND is across different search criteria: car type and colour - but I've been unable to find an example where the criteria are dynamically based on items in an array. For context, I could have dozens of schedules with 20+ class types.
You can work with a Set, isSubset will return true if the schedules element contains all elements of the searchSet
let searchSet = Set(searchArray)
var filteredSchedules = schedules.filter { searchSet.isSubset(of: $0.classTypes) }
As suggested by #LeoDabus it might be worth changing the type of classTypes to Set instead of arrays (if order doesn't matter) since they seems to be unique and then the filtering can be done in the opposite way without the need to convert searchArray each time
var filteredSchedules = schedules.filter { $0.classTypes.isSuperset(of: searchArray) }

Should be an array, but typeof says it's an object, Chrome console displays it as an array

I have this problem which may sounds stupid but I don't really understand the whys.
I declare it as a variable: let [ randomQuoterState, setrandomQuoterState ] = useState([]); Then pass it into a component inside the return: <UserOutput set={setrandomQuoterState} current={randomQuoterState} number={1}/>
The following code is inside the component:
let toSet = [];
toSet[props.number] = quoteArray[Math.floor(Math.random() * quoteArray.length)];
let quote = props.current;
if (quote[props.number]){
delete quote[props.number];
console.log("deleted")
}else {
console.log("this does not exist");
}
console.log(typeof(toSet[props.number]));
console.log(toSet[props.number].lenght)
console.log(toSet[props.number]);
quote[props.number] = toSet[props.number][Math.floor(Math.random() * toSet[props.number].lenght)];
props.set(quote);
The Consol displays it as an array, but the typeof function says its an object, and it doesn't have a length property.
I would appreciate any help or explanation, I thought about it a lot, but I couldn't come up with anything.
Arrays are objects in Javascript. In fact, there is no array type.
To see if it is an array, you should try console.log((toSet[props.number]).constructor.name) and do your checks against toSet[props.number] instanceof Array.
Do not use (toSet[props.number]).constructor.name == 'Array' in your comparisons, because you could have something that has inherited from Array but whose constructor name is different.
In JavaScript both object and array are of type Object.
In case you want to determine exact type, you can use constructor property.
const data = {};
data.contructor.name === 'Object'; // Returns True
const data = [];
data.contructor.name === 'Object'; // Returns True
data.contructor.name === 'Object'; // Returns False
Above can used to determine String, Date etc as well.
Alternatively you can use libraries like lodash which has function for these things.
However that is overkill I guess.
Hope it helps.

How can I tell the index of an array of objects in Ruby on Rails 3?

I have an array of todos [Todo1, Todo2, Todo3]
Each object has an attribute, :done_date
I need to find the first instance of the object where :done_date => null
THEN I need to know what index it is todos[N] so I can find the object before todos[N-1]
How can I do that?
You could try going about it in a slightly different way. Making use of Ruby's Enumerable#take_while:
# assuming 'todos' holds your todo objects
todos.take_while { |todo| todo.done_date != nil }.last
This will get all todo objects from todos until it sees a nil done_date, and then grab the last one. You'll have the last todo item before the first nil done_date.
So, if you have
todos = [todo1, todo2, todo3, todo4_with_null_done_date]
the code example above will return todo3.
That said, if you're really looking for something that makes use of the array's indicies, you could try something like this as well:
first_nil_index = todos.find_index { |todo| todo.done_date.nil? }
todos[first_nil_index - 1]

Resources