How do I search a string "backwards"? - javascript

I have the following code in Angular. I want to be able to find "north america" by typing "america north". How do I do that?
const getContinents = keys =>
[
'africa',
'antarctica',
'asia',
'australia',
'europe',
'north america',
'south america'
].filter(e => e.indexOf(keys.toLowerCase()) > -1);
const fakeContinentsRequest = keys =>
of(getContinents(keys)).pipe(
tap(_ => console.log(`API CALL at ${new Date()}`))
);
fromEvent(document.getElementById('type-ahead'), 'keyup')
.pipe(
debounceTime(200),
map((e: any) => e.target.value),
distinctUntilChanged(),
switchMap(fakeContinentsRequest),
tap(c => (document.getElementById('output').innerText = c.join('\n')))
)
.subscribe();

You could split() both string on spaces to get an array of each word.
Then we can use every() to check if input exist in search. This way the 'order' is not important.
Combining this with find() to search for the matching result in an array:
const options = [ 'africa', 'antarctica', 'asia', 'australia', 'europe', 'north america', 'south america' ];
function findByWord(input) {
let inputSplit = input.split(' ');
return options.filter(o => o.split(' ').every(e => inputSplit.includes(e)));
}
const res = findByWord('america north');
console.log(res)
[
"north america"
]

Simplest solution is just to reverse those words you've been given, and assume all casing is correct:
const matchStr = "I Love PHP";
const searchStr = "PHP Love";
if (matchStr.includes(searchStr.split(" ").reverse().join(" "))) {
console.log("Value matched: " + matchStr);
}
To implement into your current code, a little more annoying but you can just add an OR condition to your filter line:
.filter(e => (e.indexOf(keys.toLowerCase()) > -1) || (e.indexOf(keys.toLowerCase().split(" ").reverse().join(" ")) > -1));

I made something more dynamic, where I split the search string and then loop through them inside an Array.filter(). It's possible that it's easier to achieve this using a regexp. I also added a requirement that the search should ignore words that are shorter than two characters, and also mapped the keywords to be lowercase so they are case insensitive.
const continents = [
'africa',
'antarctica',
'asia',
'australia',
'europe',
'north america',
'south america'
]
const filterContinents = (searchStr, continents) => {
const MIN_KEYWORD_LENGTH = 2;
let keywordsArr = searchStr.split(' ')
.filter((keyword) => keyword.length > MIN_KEYWORD_LENGTH)
.map((keyword) => keyword.toLowerCase());
let hasKeywords = keywordsArr.length > 0;
const filterByKeywords = (_contintent) => {
for (const _keyword of keywordsArr) {
if (!_contintent.includes(_keyword)) {
return false
}
}
return hasKeywords
}
return continents.filter(filterByKeywords);
}
let searchStr = 'eur';
console.log({searchStr}, filterContinents(searchStr, continents));
searchStr = 'eu ro pe';
console.log({searchStr}, filterContinents(searchStr, continents));
searchStr = 'America North';
console.log({searchStr}, filterContinents(searchStr, continents));

Related

Javascript: How to combine two arrays with duplicate keys into an object

I have two arrays
array1 = [Alabama, Alabama, Georgia, Georgia, Georgia, California ]
array2 = [Mobile, Montgomery, Atlanta, Savannah, Montgomery, San Francisco ]
Both have equal number of elements and essentially for every city in Array 2, there is a corresponding state in the other array but as you can see city names in different states can be the same
I need to convert it into an Object like this in Javascript - This way I can populate a conditional drop down easily when a state is chosen unless someone has an idea to do the same with 2 distinct arrays
var citiesByState = {
Alabama: ["Mobile","Montgomery"],
Georgia: ["Savannah","Montgomery"],
California: ["San Francisco"]
}
Any help would be greatly appreciated
I have tried a few different ways but they create objects of objects as opposed to what I want above.
The Solution :
Don't forget to give a feedback.
const array1 = ['Alabama', 'Alabama', 'Georgia', 'Georgia', 'Georgia', 'California' ];
const array2 = ['Mobile', 'Montgomery', 'Atlanta', 'Savannah', 'Montgomery', 'San Francisco'];
const objProps = Array.from(new Set(array1));
const citiesByState = {};
objProps.forEach(state => {
citiesByState[state] = [];
})
array1.forEach((state, idx) => {
citiesByState[state].push(array2[idx]);
})
console.log(citiesByState);
I believe the following code solves your question.
let array1 = ["Alabama", "Alabama", "Georgia", "Georgia", "Georgia", "California"];
let array2 = ["Mobile", "Montgomery", "Atlanta", "Savannah", "Montgomery", "San Francisco"];
let citiesByState = {};
for (let [index, state] of array1.entries()) {
if (!citiesByState[state]) {
// if the state has not been added yet, add it
citiesByState[state] = [];
}
// push the corresponding city index onto the state array
citiesByState[state].push(array2[index]);
}
console.log(citiesByState);
Here is an example with reduce:
let array1 = ['Alabama', 'Alabama', 'Georgia', 'Georgia', 'Georgia', 'California' ]
let array2 = ['Mobile', 'Montgomery', 'Atlanta', 'Savannah', 'Montgomery', 'San Francisco' ]
let result = array1.reduce((memo, state, idx) => {
if(!memo[state]){
memo[state] = array2[idx];
} else {
const rest = Array.isArray(memo[state]) ? memo[state].flat() : [memo[state]]
memo[state] = [array2[idx], ...rest];
}
return memo;
}, {});
console.log(result)
Clean and fewer lines
var array1 = ["Alabama", "Alabama", "Georgia", "Georgia", "Georgia", "California", "Alabama"];
var array2 = ["Mobile", "Montgomery", "Atlanta", "Savannah", "Montgomery", "San Francisco", "test"];
const res = array1.reduce((ac,a, index) => {
let key = a;
ac[key] = ac[key] || [];
ac[key].push(array2[index])
return ac;
}, {});
console.log(res)

reduce method JavaScript, convert 2 objects in a single one

I have this array of objects:
const array = [{name: 'chips', size: 'medium'}, {name: 'burguer', size: 'large'}].
And I want to convert it to this:
{description: 'chips medium, burguer large'}
How can I do it? I have been trying with reduce without success.
You can achieve your desired result using a nested map and joins to first join each of the object values in the array and then the elements of the array into the final output:
const array = [{name: 'chips', size: 'medium'}, {name: 'burguer', size: 'large'}]
const result = { description :
array.map(obj => Object.values(obj).join(' ')).join(', ')
}
console.log(result)
Note it's possible the values in the object may not come out in the expected order (especially if you have modified the object) so it may be safer to refer to the properties directly:
const array = [{name: 'chips', size: 'medium'}, {name: 'burguer', size: 'large'}]
const result = { description :
array.map(({ name, size }) => `${name} ${size}`).join(', ')
}
console.log(result)
const array = [{name: 'chips', size: 'medium'}, {name: 'burguer', size: 'large'}];
const result = array.reduce((sum, cur) => {
if (!sum.description) {
sum.description = `${cur.name} ${cur.size}`;
return sum;
}
sum.description += `, ${cur.name} ${cur.size}`;
return sum;
}, {});
console.log(result);
You can use the Array#reduce method as follows but, as shown by #Nick using the Array#map method is more straightforward.
const array = [{name: 'chips', size: 'medium'}, {name: 'burguer', size: 'large'}],
output = array.reduce(
({description:d},{name,size}) =>
({description: (d ? `${d}, ` : "") + `${name} ${size}`}),
{});
console.log( output );
This can be implemented only using reduce method (not using maps) as below
const array = [{ name: 'chips', size: 'medium' }, { name: 'burguer', size: 'large' }]
const result = {
description: array.reduce((previousValue, currentValue) =>
Object.values(previousValue).concat(Object.values(currentValue).join(' ')), [],).toString()
}
console.log(result);
I think it is a good idea to go through "Sum of values in an object array" and "Flatten an array of arrays" in the mozilla documentation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Check if value exists in nested array

I am trying to check if a value is present in a nested array. This is what the data I am working with looks like:
[
{
name: 'bob',
occupation: ['Teacher', 'Store Owner'],
},
{
name: 'grace',
occupation: ['Doctor'],
},
]
I am trying to see if the occupation value already exists. This is my current code:
const occupationExists = (value) => users.some((user) => user.occupation === value);
I understand that this doesn't work because it isn't accessing the values in the array but how do I go about it? Any help would be appreciated.
You need to check occupation as well with Array#includes.
const occupationExists = value => users.some(user =>
user.occupation.includes(value)
);
This should do the trick, it returns true/false depending on whether the occupation exists or not.
let users = [
{
name: 'bob',
occupation: ['Teacher', 'Store Owner'],
},
{
name: 'grace',
occupation: ['Doctor'],
},
]
const occupationExists = (value) => {
let res = users.filter((user) =>
user.occupation.indexOf(value) != -1
)
return res.length > 0
}
let value = 'Doctor'
console.log(occupationExists(value))
Here is the issue with user.occupation === value: user.occupation is an array of string while value is a string, hence you cannot compare the two. You can use Array#includes() as stated by #NinaScholz or you can use another (inner) Array#some() and within it you can compare string to string: job === value.
const users = [{name: 'bob',occupation: ['Teacher', 'Store Owner']}, {name: 'grace',occupation: ['Doctor']}];
//const occupationExists = value => users.some(user => user.occupation.includes(value)); //best answer
//Alternative answer
const occupationExists = value => users.some(
user => user.occupation.some(job => job === value)
);
console.log( occupationExists('Teacher') );
console.log( occupationExists('Developer') );
let aoo = [
{
name: 'bob',
occupation: ['Teacher', 'Store Owner'],
},
{
name: 'grace',
occupation: ['Doctor', 'Store Owner'],
},
]
let fullOccupation = []
aoo.map(obj => {
obj.occupation.map(occ => {
const check = fullOccupation.includes(occ)
console.log(occ)
if(!check)
{
fullOccupation.push(occ)
}else{
alert(occ)
}
})
})

How to check if array of array contain atleast one element?

I am facing one problem in javascript filter.
Suppose this is an array1-
const array1 = [
{
title: 'Stock market news',
symbols: ['SPY.US', 'GSPC.INDX', 'DJI.INDX', 'CL.COMM', 'IXIC.INDX', 'NQ.COMM', 'ES.COMM'],
},
{
title: 'Neil Young urges Spotify',
symbols: ['SPOT.US', '639.F', '639.XETRA']
},
{
title: 'Neil Young urges Spotify',
symbols: ['AAPl.US', '639.F', '639.XETRA']
}
]
And this is an array2
const array2 = [
{Code: "AAPL"},
{Code: 'SPOT'}
]
I have to filer array1 and remove an object that not complete the condition. The condition is if the array1 symbols contain at least one element of array2 Code. I mean if the array2 Code is match arry1 symbols field at least one element.
In the above example, the result should be-
const array1 = [
{
title: 'Neil Young urges Spotify',
symbols: ['SPOT.US', '639.F', '639.XETRA']
},
{
title: 'Neil Young urges Spotify',
symbols: ['AAPl.US', '639.F', '639.XETRA']
}
]
Because this two object contain AAPL and SPOT in symbols field. I think I can clear all the things.
I am trying in this way-
const filterData = array1.filter(function (array1El) {
return !array2.find(function (array2El) {
return array1El.symbols.includes(`${array2El.Code}.US`);
})
});
But it is not working. Please say me where I am wrong.
There are two issues:
Your !array2.find condition is backwards - you want to filter to include items for which array2.find does have a match, not items for which it doesn't.
'AAPl.US' !== 'AAPL.US' - make them the same case before comparing.
It'd also be clearer to use .some instead of .find.
const array1 = [
{
title: 'Stock market news',
symbols: ['SPY.US', 'GSPC.INDX', 'DJI.INDX', 'CL.COMM', 'IXIC.INDX', 'NQ.COMM', 'ES.COMM'],
},
{
title: 'Neil Young urges Spotify',
symbols: ['SPOT.US', '639.F', '639.XETRA']
},
{
title: 'Neil Young urges Spotify',
symbols: ['AAPl.US', '639.F', '639.XETRA']
}
]
const array2 = [
{Code: "AAPL"},
{Code: 'SPOT'}
]
const filterData = array1.filter(function (array1El) {
return array2.some(function (array2El) {
return array1El.symbols
.map(s => s.toLowerCase())
.includes(`${array2El.Code.toLowerCase()}.us`);
})
});
console.log(filterData);
Or create a Set of matching symbols first, which I'd prefer for lower complexity.
const array1 = [
{
title: 'Stock market news',
symbols: ['SPY.US', 'GSPC.INDX', 'DJI.INDX', 'CL.COMM', 'IXIC.INDX', 'NQ.COMM', 'ES.COMM'],
},
{
title: 'Neil Young urges Spotify',
symbols: ['SPOT.US', '639.F', '639.XETRA']
},
{
title: 'Neil Young urges Spotify',
symbols: ['AAPl.US', '639.F', '639.XETRA']
}
]
const array2 = [
{Code: "AAPL"},
{Code: 'SPOT'}
];
const codesToFind = new Set(array2.map(({ Code }) => Code.toLowerCase() + '.us'));
const filterData = array1.filter(
({ symbols }) => symbols.some(
sym => codesToFind.has(sym.toLowerCase())
)
);
console.log(filterData);
We could use a regex alternation approach here:
const array1 = [
{
title: 'Stock market news',
symbols: ['SPY.US', 'GSPC.INDX', 'DJI.INDX', 'CL.COMM', 'IXIC.INDX', 'NQ.COMM', 'ES.COMM'],
},
{
title: 'Neil Young urges Spotify',
symbols: ['SPOT.US', '639.F', '639.XETRA']
},
{
title: 'Neil Young urges Spotify',
symbols: ['AAPL.US', '639.F', '639.XETRA']
}
];
const array2 = [{Code: "AAPL"}, {Code: "SPOT"}];
var regex = new RegExp("\\b(?:" + array2.reduce((x, y) => x.Code + "|" + y.Code) + ")\\.US");
console.log(regex); // /\b(?:AAPL|SPOT)\.US/
var output = array1.filter(x => x.symbols.some(e => regex.test(e)));
console.log(output);
The strategy here is to form a regex alternation of stock symbols, one of which is required. We then filter the original array, using some() and the regex to ensure that any match has at least one required stock symbol.
If you're doing this search multiple times, it would be best to build a symbol index and query that.
For example...
const array1 = [{"title":"Stock market news","symbols":["SPY.US","GSPC.INDX","DJI.INDX","CL.COMM","IXIC.INDX","NQ.COMM","ES.COMM"]},{"title":"Neil Young urges Spotify","symbols":["SPOT.US","639.F","639.XETRA"]},{"title":"Neil Young urges Spotify","symbols":["AAPl.US","639.F","639.XETRA"]}]
const array2 = [{Code: "AAPL"},{Code: 'SPOT'}]
// utility function to write into the index
const writeIndex = (map, key, entry) => {
const normalisedKey = String.prototype.toUpperCase.call(key)
if (!map.has(normalisedKey)) map.set(normalisedKey, new Set())
map.get(normalisedKey).add(entry)
}
const symbolIndex = array1.reduce((map, entry) => {
// convert all symbols to tokens
// eg AAPL.US becomes ["AAPL.US", "AAPL", "US"]
const keys = entry.symbols.flatMap(symbol =>
[symbol, ...symbol.split(".")])
// add the entry for each symbol token
keys.forEach(key => writeIndex(map, key, entry))
return map
}, new Map())
// pull unique items out of the index for the given codes
const queryIndex = codes => Array.from(new Set(codes.flatMap(code =>
[...symbolIndex.get(code) ?? []]
)))
console.log(queryIndex(array2.map(({ Code }) => Code)))
.as-console-wrapper { max-height: 100% !important; }
Alternatively, you can use a nested some clause with a string includes check to see if the symbol contains your code.
const array1 = [{"title":"Stock market news","symbols":["SPY.US","GSPC.INDX","DJI.INDX","CL.COMM","IXIC.INDX","NQ.COMM","ES.COMM"]},{"title":"Neil Young urges Spotify","symbols":["SPOT.US","639.F","639.XETRA"]},{"title":"Neil Young urges Spotify","symbols":["AAPl.US","639.F","639.XETRA"]}]
const array2 = [
{Code: "AAPL"},
{Code: 'SPOT'}
]
const normalise = str => str.toUpperCase()
const normalisedCodes = array2.map(({ Code }) => normalise(Code))
const filtered = array1.filter(({ symbols }) =>
normalisedCodes.some(code =>
symbols.some(symbol => normalise(symbol).includes(code))
)
)
console.log(filtered)
.as-console-wrapper { max-height: 100% !important; }

Update Array containing objects using spread operator

I have an array containing objects in javascript / typescript.
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
How can I update name of the second element (with id 2) and copy the array to a new array using javascript spread (...) operator?
You can use a mix of .map and the ... spread operator
You can set the value after you've created your new array
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {return {...a}})
array2.find(a => a.id == 2).name = "Not Two";
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or you can do it in the .map
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {
var returnValue = {...a};
if (a.id == 2) {
returnValue.name = "Not Two";
}
return returnValue
})
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Spred Operator, you can update particular array value using following method
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const label = "name";
const newValue = "Two Updated";
// Errow comes if index was string, so make sure it was integer
const index = 1; // second element,
const updatedArray = [
...array.slice(0, index),
{
// here update data value
...array[index],
[label]: newValue,
},
...array.slice(index + 1),
];
console.log(updatedArray);
There are a few ways to do this. I would suggest using Array.map :
let new_array = array.map(element => element.id == 2 ? {...element, name : 'New Name'} : element);
or with Object.assign :
let new_array = array.map(element => element.id == 2 ? Object.assign({}, element, {name : 'New Name'}) : element);
Map returns a new array, so you shouldn't need the array spread operator.
We can use
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = [...array]
array2.find(a => a.id == 2).name = "Not Two";
console.log(array2);
You can simply use map() and change the element there.
here is the code---
array_copy = array.map((element) => {
console.log(element.id);
if (element.id === 2) {
element.name = "name changed";
}
return element;
});
console.log(array_copy);
Here the main array also gets modified, as elements inside the array are objects and it references to the same location even in the new array.
You can do it like this in map, no need for spread:
const array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
const updatedArray = array.map(a => {
if (a.id == 2) {
a.name = 'New Name';
}
return a;
});
Merging properties from filterQueryParams to selectedLaws (existing solutions did not suit me):
if (this.filterQueryParams && Object.prototype.toString.call(this.filterQueryParams) === '[object Array]') {
for (const law of this.filterQueryParams) {
if (law as Laws.LawDetail) {
const selectedLaw = this.selectedLaws.find(x => x.languageCode === law.languageCode);
if (selectedLaw) {
for (const propName of Object.keys(law)) {
selectedLaw[propName] = law[propName];
}
}
else {
this.selectedLaws.push(law);
}
}
}
}
import React,{useState} from 'react';
export function App(props) {
const[myObject,setMyObject] = useState({
"Name":"",
"Age":""
});
const[myarray, setmyarray] = useState([]);
const addItem =() =>{
setMyObject({...myObject,"Name":"Da","Age":"20"});
setmyarray([...myarray, 1]);
};
console.log(myarray);console.log(myObject);
return (
<div className='App'>
<h1>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={addItem}>Add me</button>
</div>
);
}
// Log to console
console.log('Hello console')
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 =[...array.slice(0, 0), Object.assign({}, array[0], {
name:'new one' //change any property of idx
}),...array.slice(0 + 1)]
console.log(array);
console.log(array2);
[...array.slice(0, idx), Object.assign({}, array[idx], {
x:new_x //change any property of idx
}),...array.slice(idx + 1)]

Categories

Resources