I have an array I want to use as base array of my work, and each time a unction calls I want it to be cloned to prevent changing the base one, but it doesn't work I don't know why and I tried all other solutions else where
this is my code:
const baseArray = [
{
english: {
name: "test1 english",
description: "test1 english"
},
spanish: {
name: "test1 spanish",
description: "test1 spanish"
}
}
];
function removeSpanish() {
// showing base array before work
console.log(baseArray); // expected to log the baseArray, but logs the edited array
// cloning the array
var result = [...baseArray];
// checking if passed by value or passed by reference
console.log(result[0] === baseArray[0]); // expected to log false, but it logs true
// removing other language details from each collection
result.map(collection => {
delete collection['spanish'];
return collection;
});
// showing base array after work
console.log(baseArray); // expected to log the baseArray, but logs the edited array
return result;
}
export { find };
and what surprises me is that even the first
console.log(baseArray)
shows the edited one.
and the
console.log(result[0] === baseArray[0]);
logs true so they are passed by refference
i also tried these 2 other approaches but none of them worked
var result = baseArray.splice();
var result = Array.from(baseArray);
NOTE: i use this in react and it will be excuted each time a state changes, I don't know if it causes the problem
thanks to Charles Bamford that suggested using lodash's cloneDeep, and thanks to Robin Zigmond that suggested using forEach instead of map
so i updated my code to this and it worked:
const _ = require('lodash');
const baseArray = [
{
english: {
name: "test1 english",
description: "test1 english"
},
spanish: {
name: "test1 spanish",
description: "test1 spanish"
}
}
];
function removeSpanish() {
// showing base array before work
console.log(baseArray);
// cloning the array
var result = _.cloneDeep(Collections);
// checking if passed by value or passed by reference
console.log(result[0] === baseArray[0]);
// removing other language details from each collection
result.forEach(collection => {
delete collection['spanish'];
return collection;
});
// showing base array after work
console.log(baseArray);
return result;
}
export { find };
you can read more about lodash here
Related
I receive JSON data from the service, but the keys change in the data with each request, below I will give an example in three versions.
Exmaple 1:
{
"trackingPayloads": {
"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 2:
{
"trackingPayloads": {
"36tW7DqZ3H9KKBEAumZmowmUwmDRmVCjQgv5zi9GM3Kz": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"OgtE51n3YtvrVXWLFjPmpnRt2k5DExF7ovxmBTZrZ6wV": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 3:
{
"trackingPayloads": {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}'",
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
As you can see, the data included in the keys does not change since I am requesting the same information, but the key will change with each request.
Please help, what are the options to get the data Title, Index and any other content in these keys using node js?
Only one option came to my mind - to rename the keys upon receipt in 1,2,3 ... and then get data from them, but this needs to be done dynamically, since about 120 requests per minute are made, you need to get this data quickly, there are no options to save it to a file (I didn’t understand how)
UPDATE, added my code.
I am attaching an example of my code, the idea is to eventually get the data I need from the right keys from trackingPayloads, please help with the code <3
const AwaitAPIResponse = await ProductAPI(product_sku);
const $ = cheerio.load(AwaitAPIResponse);
const JSONDATA = [];
$('pre').each(function() {
JSONDATA.push($(this).text());
});
const ProductJson = JSON.parse(JSONDATA[0]) // this is where I get all the data
const MainJson = ProductJson["trackingPayloads"] // here I go to the trackingPayloads you saw above
How can I get the data I need?
You can use Object.keys() to get all the different keys of an object and use a loop to go through them.
Therefore, you can rework this code in such a way that each of the values is stored as an element in an array, maybe makes the data easier to work with:
const convert = object => {
const ret = []
for (const key of Object.keys(object)) {
ret.push(object[key])
}
return ret
}
This will give you following result for your use case:
[{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
{"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
The way you'd call this is as follows:
const some_parsed_json = {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
},
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
}
}
const json_object_values = convertor(some_parsed_json)
If you don't car about the key you could use Object.values on the received object to get the values
Object.values(payload)
// With your example it will return:
// [{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
// {"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
or in a more complete example
async function getParsedValues() {
const responseString = await service.getData(); // '{"trackingPayloads":{"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq":{"title":"Red Shoes","index":3,"id":"17777","type":"category"},"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk":{"title":"White Shoes","index":3,"id":"17777","type":"category"}}}'
const parsedResponse = JSON.parse(responseString); // { trackingPayloads: { Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq: { title:'RedShoes', index: 3, id: '17777', type: 'category' }, ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk:{title:'WhiteShoes', index: 3, id: '17777', type: 'category' } }}
const values = Object.values(parsedResponse); // [{"title":"Red Shoes","index":3,"id":"17777","type":"category"}, {title:'WhiteShoes', index: 3, id: '17777', type: 'category' }]
return values;
}
I am having a hard time filtering through an array of objects based on a value in a nested array of objects. I have a chat application where a component renders a list of chats that a user has. I want to be able to filter through the chats by name when a user types into an input element.
Here is an example of the array or initial state :
const chats= [
{
id: "1",
isGroupChat: true,
users: [
{
id: "123",
name: "Billy Bob",
verified: false
},
{
id: "456",
name: "Superman",
verified: true
}
]
},
{
id: "2",
isGroupChat: true,
users: [
{
id: "193",
name: "Johhny Dang",
verified: false
},
{
id: "496",
name: "Batman",
verified: true
}
]
}
];
I want to be able to search by the Users names, and if the name exists in one of the objects (chats) have the whole object returned.
Here is what I have tried with no results
const handleSearch = (e) => {
const filtered = chats.map((chat) =>
chat.users.filter((user) => user.name.includes(e.target.value))
);
console.log(filtered);
// prints an empty array on every key press
};
const handleSearch = (e) => {
const filtered = chats.filter((chat) =>
chat.users.filter((user) => user.name.includes(e.target.value))
);
console.log(filtered);
// prints both objects (chats) on every keypress
};
Expected Results
If the input value is "bat" I would expect the chat with Id of 2 to be returned
[{
id: "2",
isGroupChat: true,
users: [
{
id: "193",
name: "Johhny Dang",
verified: false
},
{
id: "496",
name: "Batman",
verified: true
}
]
}]
The second approach seems a little closer to what you're trying to accomplish. There's two problems you may still need to tackle:
Is the search within the name case insensitive? If not, you're not handling that.
The function being used by a filter call needs to return a boolean value. Your outer filter is returning all results due to the inner filter returning the array itself and not a boolean expression. Javascript is converting it to a "truthy" result.
The following code should correct both of those issues:
const filtered = chats.filter((chat) => {
const searchValue = e.target.value.toLowerCase();
return chat.users.filter((user) => user.name.toLowerCase().includes(searchValue)).length > 0;
});
The toLowerCase() calls can be removed if you want case sensitivity. The .length > 0 verifies that the inner filter found at least one user with the substring and therefore returns the entire chat objects in the outer filter call.
If you want to get object id 2 when entering bat you should transform to lowercase
const handleSearch = (e) =>
chats.filter(chat =>
chat.users.filter(user => user.name.toLowerCase().includes(e.target.value)).length
);
try this it should work
const handleSearch2 = (e) => {
const filtered = chats.filter((chat) =>
chat.users.some((user) => user.name.includes(e))
);
console.log(filtered);
};
filter needs a predicate as argument, or, in other words, a function that returns a boolean; here some returns a boolean.
Using map as first iteration is wrong because map creates an array with the same number of elements of the array that's been applied to.
Going the easy route, you can do this.
It will loop first over all the chats and then in every chat it will check to see if the one of the users' username contains the username passed to the function. If so, the chat will be added to the filtered list.
Note, I am using toLowerCase() in order to make the search non case sensitive, you can remove it to make it case sensitive.
const handleSearch = (username) => {
var filtered = [];
chats.forEach((chat) => {
chat.users.forEach((user) => {
if (user.name.toLowerCase().includes(username.toLowerCase())) {
filtered.push(chat);
}
});
});
console.log(filtered);
return filtered;
}
handleSearch('bat');
I'm confused by this section of the immer docs. I created a simpler example to test the same principle, i.e. to add some new object to the draft data structure and then modify it. Which according to the docs should also modify the original data structure:
import { produce } from "immer";
function print(obj) {
console.log(JSON.stringify(obj));
}
var todos = [
{ id: 0, done: false },
{ id: 1, done: false }
];
function onReceiveTodo(todo) {
return produce(todos, draft => {
draft[1] = todo;
draft[1].done = true;
});
}
let nextTodos = onReceiveTodo({ id: 3, done: false });
print(todos);
print(nextTodos);
// [{"id":0,"done":false},{"id":1,"done":false}] // todos
// [{"id":0,"done":false},{"id":3,"done":true}] // nextTodos
But this seems to work.
Could someone please explain this pitfall in a bit more detail?
Note: I also tried to use the exact same example that was in the docs, but was confused about what the data structure todos is supposed to look like. Since draft is bound to the original version of todos, this line: draft.todos[todo.id] = todo suggests that we have something like todos = {todos: {0: {done: false}, 1: {done: false},...}, but todo.id suggests that a todo object contains the id.
It's about "if you pass and mutate an object inside produce it is in fact mutated".
Could someone please explain this pitfall in a bit more detail?
Basically when you do:
let nextTodos = onReceiveTodo({ id: 3, done: false });
The original object can be changed. Consider instead:
const todo = { id: 3, done: false };
let nextTodos = onReceiveTodo(todo);
todo.done; // true, this object was mutated outside of Immer
i want to implement jasmine test with the next code:
This is my filter-service.js
function FilterService() {
}
FilterService.prototype.filter = function (companies, filter) {
return companies;
};
And my filter-service-spec.js is
describe("Filter service filter(...) tests", function() {
var filterService = new FilterService();
var allTestCompanies= [
{ id: 1, name: "company1 Test", admin: "Test Admin" },
{ id: 2, name: "company2", admin: "Test Admin", country: 'London' },
{ id: 3, name: "company3", admin: "Mery Phill" }
];
it('returns original collection if called with undefined filter', function() {
var input = [1, 2, 3];
var result = filterService.filter(input, undefined);
expect(result).toEqual(input);
});
it('returns original collection if called with empty filter', function () {
var input = [2, 6, 7];
var result = filterService.filter(input, '');
expect(result).toEqual(input);
});
it('only includes matching companies once', function() {
var result = filterService.filter(allTestCompanies, 'Test');
expect(result.length).toEqual(2);
});
it('matches exact text on company name', function() {
var result = filterService.filter(allTestCompanies, "company1 Test");
expect(result[0]).toEqual(allTestCompanies[0]);
});
it('matches exact text contained in company name', function () {
var result = filterService.filter(allTestCompanies, "Test");
expect(result[0]).toEqual(allTestCompanies[0]);
});
it('matches case invarient text contained in company name', function () {
var result = filterService.filter(allTestCompanies, "test");
expect(result[0]).toEqual(allTestCompanies[0]);
});
it('matches exact text of admin', function() {
var result = filterService.filter(allTestCompanies, 'Mery Phill');
expect(result[0]).toEqual(allTestCompanies[2]);
});
it('matches exact text in admin', function () {
var result = filterService.filter(allTestCompanies, 'Phil');
expect(result[0]).toEqual(allTestCompanies[2]);
});
it('matches case invarient text in admin', function () {
var result = filterService.filter(allTestCompanies, 'PHIl');
expect(result[0]).toEqual(allTestCompanies[2]);
});
});
How i can implement a function in filter-service.js for pass the javascript tests. For now only pass the 2 first.
Sure, this is actually very easy to do, but I will provide just with approach you should take. So lets begin ...
I hope you have an environment where you able to run your tests (Jasmine installed and you able to run your tests against "FilterService" object and "filter" method in particular). You said two first methods are succeeded.
Each Jasmine "it" function has description, for example "matches exact text on company name" (test #4). This is most important part you will be dealing with. We will talk about this test. Ask yourself what this description tells you? This means if you pass into "filter" method exact name of the company, the function should go through array of given companies objects and try to find match for exact company name.
Next you would need to look at implementation of Jasmine "expect" function. You will notice that returned result from the "filter" function call must be equal to the first element of the given array of companies because the name of the company in the filter parameter match to this object's "name".
After you implement this part and test succeeded, you would go to next test and add/change existing implementation to accomplish next "it" description.
This is called TDD (test driven development). Please read a bit on it before you start working.
Finally lets try to implement this test #4. Please note the code provided may not work, but as we agreed you will make it work, I just show the approach ...
function FilterService() {
}
FilterService.prototype.filter = function (companies, filter) {
// this is our result variable which we will return at the end
var result;
// for test #1 the "filter" parameter is undefined and the test description says "returns original collection if called with undefined filter" (#2 will look similar to test #1, add this by yourself)
if (typeof filter === "undefined") {
result = companies;
}
// jump to test #4 we are talking about. "it" says "matches exact text on company name"
companies.forEach( function (companyObj) {
if (companyObj.name === filter) {
result = [companyObj];
return false;
}
});
// continue to modify the function to meet all criteria of every "it" description
// we return our result
return result;
};
If I have the following object:
var record = {
title: "Hello",
children: [
{
title: "hello",
active: true
},
{
title: "bye",
active: false
}
};
I want to use underscore to determine if one of the children within the record has or does not have a title equal to a variable that will come from a form post, but also needs to be case insensitive... So for example:
var child = { title: "heLLo", active: true }
And underscore ( and this is wrong, and what I need help with ):
if ( _.contains(record.children, child.title) ) {
// it already exists...
} else {
// ok we can add this to the object
}
So basically I don't understand how to do this with underscore when dealing with array objects that have multiple key/value pairs. Also what is the best method for ignoring case? Should this be done in the underscore _.contains function? Regex? Use toLowerCase() beforehand to create the variables? If someone types in any variation of "Hello", "HELLO", "heLLO", etc. I don't want the insert to take place.
Thank you!
Use _.find and RegExp with "i" case-ignore flag
var valueFromPost = "bye";
var someOfChildrenHasValueFromPost = _.find(record.children,function(child){
return child.title.match(new RegExp(valueFromPost,"i"));
});
Update
Here is an example #JSFiddle
JS code:
record = {
children:[
{title:'bye'},
{title:'Bye'},
{title:'Hello'}
]
}
var testValue = function(value) {
return _.find(record.children,function(child){
return child.title.match(new RegExp(value,"i"));
});
}
console.debug(testValue('Bye')); //returns object with "Bye" title
console.debug(testValue('What'));//returns undefined
console.debug(testValue('bye')); //returns object with "bye" title