I'm using react and lodash. I have a prop type {props.value} that returns the result of "ThisIsFoo" and other results such as "ThisIsBar". I want to be able to map these results to elegantly display the output as "This is foo". Some sort of look up array will do the trick where I can list all the possible outcomes.
e.g.
var KeyMap = {
ThisIsFoo : "This is foo",
ThisIsBar : "This is Bar",
anotherOutcomeHere: "Another outcome here"
};
Ideally I want to get lodash to look at {props.value} match them to the key is it exists then output with its replacement.
var KeyMap = {
slowFit : "Slow Fit",
fastFit : "Fast Fit",
slowUnfit : "Slow Unfit",
fastUnfit : "Fast Unfit",
};
const str = props.label.replace(/slowFit|fastFit|slowUnfit|fastUnfit/gi, function(matched){
return KeyMap[matched];
});
This will create the keyMap from the values and then you can just match by the key:
var data = ["This is foo", "This is Bar", "Another outcome here"]
var keyMap = _.reduce(data, (r,c) => Object.assign(r, {[c.replace(/ /g, "")]: c}), {})
console.log("KeyMap: ", keyMap)
console.log(keyMap['ThisisBar'])
console.log(keyMap['Thisisfoo'])
// In your casse you would just display {keyMap[props.value]}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Related
I want to add a new map field inside another map field.
My data structure looks like this:
I want to add a 'field b' after 'field a' inside 'Filed 1' (pardon my typo).
my code looks like this
const collsRef = doc(db, "collections", collectionUid); // get the right document
await setDoc(collsRef, { sellectData }, { merge: true });
but not only this is giving me an error, I believe this code would only add a new field at the same level as 'Filed 1'
the data type triggering the unsupported field value error is an JS object like so – do I need to parse it before comiting it to the db?
I've blanked out data and left the structure and syntax because it contained personal sensitive data. I hope it's enough to explain the problem.
many thanks in advance
I'm not sure what the sellectData structure looks like but you have to construct an object that has the same structure from Firestore. See sample code below:
const collsRef = doc(db, "collections", collectionUid);
let sellectData = {
"field b": { "key b" : "value b" }
}
await setDoc(collsRef, {
"Filed 1": sellectData
}, { merge: true });
This would result in (already corrected your typo):
Update:
You have two options to set the object key as a variable.
You need to make the object first, then use [] to set it.:
let key = "Field 1";
let sellectData = {
"field b": { "key b" : "value b" }
}
await setDoc(collsRef, {
[key]: sellectData
}, { merge: true });
Or, by contructing the object itself.
let key = "Field 1";
let sellectData = {
[key] : { "field b":
{ "key b" : "value b" }
}
}
// Remove the curly brackets `{}` in this case.
await setDoc(collsRef,
sellectData,
{ merge: true });
For more information, see Update fields in nested objects.
I have 2 arrays both with Strings.
let exclude = ["text 1", "text 2", "APP", "John"]
let array2 = ["this is text 1", "This is text 2", "This App is under development", "John is working on this project", "This is great"]
Im trying to filter any text that is contained in exclude from array2, caseInsensitive.
So in this example it should print "This is great"
Instead of using multiple lines for each filter like:
let filter = array2.filter{!$0.contains("APP")}
I tried:
var filter = array2.filter({exclude.contains($0)})
but it does not filter.
Any advice would be greatly appreciated
With "explicit" return and no $0.
let filtered = array2.filter { aString in
return !exclude.contains(where: { anExcludedString in
return aString.range(of: anExcludedString, options: .caseInsensitive) != nil
})
}
Why var filter = array2.filter({exclude.contains($0)}) didn't work?
First issue:
There is no case insensitive check.
Second issue:
You are using contains() on a [String], not a String. So it expect full equality between the two strings. So if array2 was ["APP"], it would have worked.
For instance if you had:
let exclude = ["text 1", "text 2", "APP", "John"]
let array2 = ["this is text 1", "This is text 2", "This App is under development", "John is working on this project", "This is great", "This APP is under development"]
let filtered = array2.filter { aString in
return !exclude.contains(where: { anExcludedString in
return aString.contains(anExcludedString)
})
}
Then "This APP is under development" would have been removed.
Now, going back to the initial answer, the way to check case insentive is to use range(of:options:).
I’m building search functionality. I have an array of User objects and each User has a dictionary of tags. I’m trying to return users with searched tags.
My user class is:
class User: NSObject {
var name: String?
var tags = Dictionary<String, String>()
}
An example of the tags is:
tags: {
“entry1” : “test1”,
“entry2” : “test2”,
“entry3” : “test3”
}
I’ve been trying variances of:
let predicate = NSPredicate(format: “tags contains[c] %#", “test1”);
let filteredArray = self.usersArray!.filter { predicate.evaluate(with: $0) }; print(“users = ,\(filteredArray)");
It’s throwing “this class is not key value coding-compliant for the key tags.’ I'm not sure how to search inside the User object.
Update:
I've change the dictionary to an array and
filter { $0.tags.contains(searchTerm) } works for exact search match, as suggested.
.filter( {$0.tags.reduce(false, {$1.contains(searchTerm)} )} ) does not work for a partial match however, which is what i'm looking for.
Here is what is printed:
Search term: ale
Result: []
Tags array inside each object:
["Alex", "Will", ""]
["Bob", "Dan", ""]
["First", "Second", "Third"]
["Testing 1", "Testing 2", ""]
Your implementation of tags seems cumbersome. Here is a simpler one.
class User {
var name: String
var tags: [String]
}
Now let's say you have an array of users in users.
var users: [User]
// Store users in the array
Now, this is how you check which user contains a particular tag.
let tag = "yourTag"
users.filter( { $0.tags.contains(tag) } )
However, if you are adamant that you need the tags in a dictionary, then you could do it like this,
users.filter( {$0.tags.values.contains(tag)} )
If you want to check if the tag is part of any of the tags in a particular user, you could do it like this,
let filteredUsers = users.filter( {$0.tags.reduce(false, {$1.contains(tag)} )} )
P.S - we don't use ; in Swift.
It's good practice to make your models conform to Codeable:
struct User: Codable {
var userId: UUID
var name: String = ""
var referenceTags: [String: String] = [:]
}
// Create some users:
let Bob = User(userId: UUID(), name: "Bob", referenceTags: ["tag1": "no", "tag2": "yes"])
let Steve = User(userId: UUID(), name: "Steve", referenceTags: ["tag2": "no", "tag3": "no"])
Create an array of users to filter.
let users:[User] = [Bob, Steve]
Get a search predicate or string from your Search bar or other.
let searchPredicate = "yes"
Filter and iterate over tags for the required value.
let results = users.filter { (user) -> Bool in
for (key, _) in user.referenceTags.enumerated() {
if (user.referenceTags[key] == searchPredicate) {
return true
}
}
}
If I had an array of objects
var obj = [{
text: "Hello my name is none of your business"
},
{
text: "This is the second text of this object, hello"
},
{
text: "This is the third text of this object, hello"
},{
text: "This is a copy of the third text of this object, hello"
}];
How could I filter out all objects that contain keywords not necessarily in order? An jsfiddle is provided for further clarification
e.g.
user searches "this", filter returns second,third, and fourth text
user searches "third copy", filter returns just the fourth text,
The problem is that using the basic filter (an example below) seems to search for exact phrases. e.g. "third copy" will return an empty result. The user has to type in "copy of the third" to get a result back.
JSFiddle: http://jsfiddle.net/mpm2uamf/
I was think of using the split function on all single whitespace and then somehow loop through that using Regex...but I clearly have no idea what I'm doing
.filter('searchBody', function(){
return function(elements, input){
if(input !== undefined){
var inputs = input.split(" ");
}
var filtered = [];
// loop through user inputs and make them optional in RegExp?
var letterMatch = new RegExp(inputs, 'i');
for(var i = 0; i < elements.length; i++){
var element = elements[i];
if( letterMatch.test(element.body)){
filtered.push(element);
}
}
return filtered;
};
})
Could someone give me hints or an explanation of how to do this?
I've created a custom filter that does that:
app.filter('multipleTags', function($filter) {
return function multipleTags(items, predicates) {
predicates = predicates.split(',')
angular.forEach(predicates, function(predicate) {
items = $filter('filter')(items, predicate.trim())
})
return items;
}
})
http://plnkr.co/edit/ZhnuM0WW3GZS5QLxK98t?p=preview
although my code was designed for comma separated tags it can be reused for spaces in your case
I am dealing with documents like the following one:
> db.productData.find({"upc" : "XXX"}).pretty()
{
"_id" : ObjectId("538dfa3d44e19b2bcf590a77"),
"upc" : "XXX",
"productDescription" : "bla foo bar bla bla fooX barY",
"productSize" : "",
"ingredients" : "foo; bar; foo1; bar1.",
"notes" : "bla bla bla"
}
>
I would like to have a document containing, among the fields, a list/array of splitted ingredients (on the ;). I want to split the string of the original collection into an array of strings.
I would like to map only some of the input fields in the output collection.
I would like to use mapreduce on MongoDB.
I've tried many different ways moving stuff from the map function to the reduce function failing to find a proper solution.
From all the attempts I performed, now I know I need to check for null values etc, so the following one is my last attempt:
The map function:
var mapperProductData = function () {
var ingredientsSplitted = values.ingredientsString.split(';');
var objToEmit = {barcode : "", description : "", ingredients : []};
// checking for null (is this strictly necessary? why?)
if (
this.hasOwnProperty('ingredients')
&& this.hasOwnProperty('productDescription')
&& this.hasOwnProperty('upc')
) {
for (var i = 0; i < ingredientsSplitted.length; i++) {
// I want to emit a new document only when I have all the splitted strings inside the array
if (i == ingredientsSplitted.length - 1) {
objToEmit.barcode = this.upc;
objToEmit.description = this.productDescription;
objToEmit.ingredients = ingredientsSplitted;
emit(this.upc, objToEmit);
}
}
}
};
The reduce function:
var reducerNewMongoCollection = function(key, values) {
return values;
};
The map-reduce call:
db.productData.mapReduce(
mapperProductData,
reducerNewMongoCollection,
{
out : "newMongoCollection" ,
query: { "values" : {$exists: true} }
}
);
I am getting an empty collection in output (newMongoCollection is empty).
What am I doing wrong?
Let's start from the beginning. Your map function should look like this:
var mapperProductData = function () {
var ingredientsSplitted = this.ingredients.split(';');
var objToEmit = {
barcode : this.upc,
description : this.productDescription,
ingredients : ingredientsSplitted
};
emit(this.upc, objToEmit);
};
Your map-reduce call should be:
db.productData.mapReduce(
mapperProductData,
reducerNewMongoCollection,
{
out : "newMongoCollection",
query : {
upc : { $exists : true },
productDescription : { $exists : true },
ingredients : { $exists : true , $type : 4 }
}
}
);
The query part will filter the documents that do have relevant fields. Also the query parameter $type will match only documents where ingredients is an array. This way you don't need to do complicated checking inside your map function and the number of documents sent to map function will be lower.
The result for your test document document will look like this:
key : XXX,
value: {
"barcode" : "XXX",
"description" : "bla foo bar bla bla fooX barY",
"ingredients" : [
"foo",
" bar",
" foo1",
" bar1."
]
}