Related
I want to check if there is a value in a certain variable I have. Below I put an example of a logic that I want to achieve.
No matter how hard I tried, I was able to write a very sketchy code as a result of 3 hours of work and research, but it has nothing to do with what I want to achieve.
My Code:
const Files = [
{
Name: 'System',
Type: 'directory',
Value: [
{
Name: 'Main',
Type: 'directory',
Value: [
{
Name: 'Drivers',
Type: 'directory',
Value: [
{
Name: 'Startup',
Type: 'file',
Value: new FileSystem.File('Startup', 0x1, 'test blah blah'),
},
],
},
],
},
],
},
];
BlahBlah.has(Files, 'System->Main->Drivers');
// [File]
BlahBlah.has(Files, 'System->Main->Drivers->Startup');
// File
BlahBlah.has(Files, 'System->Main->Drivers->AnyWhere');
// undefined
BlahBlah.has(Files, 'System->Main->AnyRandomDirectory');
// NaN
My Function:
function text2Binary(str: string, spliter: string = ' '): string {
return str
.split('')
.map(function (char) {
return char.charCodeAt(0).toString(2);
})
.join(spliter);
}
export function FileTypeFromNumber(e: number) {
if (typeof e != 'number')
try {
e = Number(e);
} catch (_) {
return null;
}
return {
0x1: {
Name: 'Executable File',
Extension: 'exe',
},
0x2: {
Name: 'Text Document',
Extension: 'txt',
},
}[e];
}
export type FileTypes =
| 0x1
| 0x2
| 0x3
| 0x4
| 0x5
| 0x6
| 0x7
| 0x8
| 0x9
| null;
export class File {
Name: string;
Type: {
Name: string;
Extension: string;
};
Content: string;
Size: number;
constructor(name: string, type: FileTypes, content: string) {
this.Name = name;
this.Type = FileTypeFromNumber(type);
this.Content = content;
this.Size = text2Binary(content, '').length;
}
}
export class Directory {
public Name: string;
public Files: (File | Directory)[] = [];
constructor(name: string) {
this.Name = name;
}
addFile(file: File | Directory) {
this.Files.push(file);
}
getFile(name: string): null | (File | Directory)[] {
if (typeof name != 'string')
try {
name = String(name);
} catch (_) {
return null;
}
const Result = this.Files.filter((e) => e.Name == name);
return Result.length == 0 ? null : Result;
}
getSize() {
return this.Files.map((e) =>
e instanceof Directory ? e.getSize() : e.Size
).reduce((a, b) => a + b, 0);
}
has(name) {
return this.Files.some((e) => e.Name == name);
}
getJSON() {
return this.Files.map((e) => ({ ...e }));
}
}
interface x {
Content: string;
Name: string;
Size: number;
Type: string;
}
export function ConvertFromJSONtoDirectory(json: any[]) {
return json.map((value) => {
const isDirectory = value.Type == 'directory';
if (!isDirectory) {
return value.Value;
}
const self = new Directory(value.Name);
ConvertFromJSONtoDirectory(value.Value).map((e) => self.addFile(e));
return self;
});
}
export default class DirectorySystem {
Memory: Map<any, any>;
Current: string | null;
constructor(Current = null) {
this.Memory = new Map();
this.Current = Current;
}
addDirectory(directory: Directory): null | true {
if (!(directory instanceof Directory)) return null;
if (this.Memory.has(directory.Name)) return null;
this.Memory.set(directory.Name, directory);
return true;
}
getDirectory(DirectoryName: string): boolean | Directory {
if (typeof DirectoryName != 'string')
try {
DirectoryName = String(DirectoryName);
} catch (_) {
return null;
}
const Result = this.Memory.has(DirectoryName);
return Result ? this.Memory.get(DirectoryName) : Result;
}
getDirectoryCurrent() {
if (this.Current == null) return this;
}
changeDirectory(by: -1 | 1, value: string) {
if (by == -1) {
if (this.Current == null) return null;
if (this.Current.includes('->')) {
this.Current = this.Current.split('->').slice(0, -1).join('->');
} else {
this.Current = null;
}
return this.Current;
} else if (by == 1) {
let Position = [this.Current, value].join('->');
if (this.Current == null) {
Position = Position.split('->').slice(1).join('->');
}
let Result = this.has(Position);
console.log(Result);
}
}
has(query: string) {
try {
return query.split('->').reduce((a, b) => {
if (Array.isArray(a)) {
const f = a.filter((e) => e['Name'] == b);
if (a.length > 0) {
return f['Files'];
} else {
return a;
}
}
return a['Files'];
}, this.getJSON());
} catch (_) {
return false;
}
}
getJSON(): x[][] {
return [...this.Memory.values()].reduce((a, b) => {
a[b.Name] = b.getJSON();
return a;
}, {});
}
}
Result: (Thanks Michael M. and chill 389cc for helping me understand the error)
has(
query: string,
overwrite = null
) {
// If overwrite argument is not null, we are going use it.
let files = overwrite == null ? this.getJSON() : overwrite;
// Split string for getting more usable type with converting string to Array.
const QueryParams = query.split('->').filter(String);
// If we dont have no query, we can return current status.
if (QueryParams.length == 0) return overwrite;
if (Array.isArray(files)) {
const SearchFor = QueryParams.shift();
const Result = files.filter((e) => {
if (e instanceof Directory) {
const x = e.Name == SearchFor;
return x ? e : false;
}
return e.Name == SearchFor;
})[0];
// If we cant find any indexing result
if (!Result) return false;
// We found a file and if we dont have any query this is mean we found it!
if (Result instanceof File) return QueryParams.length == 0;
// We found a Directory and we doesnt have any Query now, so we can return true.
if (Result instanceof Directory && QueryParams.length == 0) return true;
if (
Result.Name != SearchFor ||
(QueryParams.length != 0 && Result.Files.length == 0)
)
// If name not suits or still we has Query and not enough file for indexing.
return false;
// If nothing happens on upper section, return rescyned version of this function.
return this.has(QueryParams.join('->'), Result.Files);
} else {
// If value is Object, Try Search param in object, and return it.
const Result = files[QueryParams.shift()];
return !Result ? false : this.has(QueryParams.join('->'), Result);
}
}
I can't replicate all of your code, but does this help?
interface Entry {
Name: string,
Type: string,
Value: Array<Entry> | any,
};
const Files = [
{
Name: "System",
Type: "directory",
Value: [
{
Name: "Main",
Type: "directory",
Value: [
{
Name: "Drivers",
Type: "directory",
Value: [
{
Name: "Startup",
Type: "file",
Value: "test", // change this to anything
},
],
},
],
},
],
},
];
function getEl(files: Array<Entry>, path: String) {
let path_walk = path.split("->");
let obj = files;
for (let part of path_walk) {
let found = false;
for (let entry of obj) {
if (entry.Name == part) {
obj = entry.Value;
found = true;
}
}
if (!found) return undefined;
}
return obj;
}
console.log(getEl(Files, "System->Main->Drivers")); // => [ { Name: 'Startup', Type: 'file', Value: 'test' } ]
console.log(getEl(Files, "System->Main->Drivers->Startup")); // => "test"
console.log(getEl(Files, "System->Main->Drivers->AnyWhere")); // => undefined
console.log(getEl(Files, "System->Main->AnyRandomDirectory")); // => undefined
There are some obvious problems, such as the fact that your example shows .has() being called with two arguments but it is defined in the class to only take in one. That being said, here is a function that, given a string query as you have and an array of objects like you have, would read the query and return if the array works for that query.
function has(fileSystem, query) {
const arrayOfArgs = query.split('->')
if (Array.isArray(fileSystem)) {
for (let i = 0; i < fileSystem.length; i++) {
if (fileSystem[i]['Name'] === arrayOfArgs[0]) {
if (arrayOfArgs.length === 1) {
// element found
return true; // replace this to return an actual value if that is desired.
}
if (fileSystem[i]['Type'] === 'directory') {
// if not, recurse in if it is a directory
return has(fileSystem[i]['Value'], arrayOfArgs.slice(1).join('->'));
} else {
// if it isn't a directory, don't try to recurse in
return false;
}
}
}
}
return false;
}
console.log(has(Files, 'System->Main->Drivers')); // true
console.log(has(Files, 'System->Main->Drivers->Startup')); // true
console.log(has(Files, 'System->Main->Drivers->AnyWhere')); // false
console.log(has(Files, 'System->Main->AnyRandomDirectory')); // false
You'll have to add your own types to get it back to TypeScript and obviously I pulled it out of the class for easier testing but it should be pretty easy to re-implement.
Cant understand why do i need to write return inside each if and else, but not once after conditional statement`s block?
Proper return use
let arrayToList = arr => {
if(arr.length == 1)
{
let list = {
value: arr.shift(),
rest: null
};
return list;
}
else{
let list = {
value: arr.shift(),
rest: arrayToList(arr)
};
return list;
}
};
console.log(arrayToList([10, 20]));
//{value: 10, rest: {value: 20, rest: null}}
Misuse
let arrayToList = arr => {
if(arr.length == 1)
{
let list = {
value: arr.shift(),
rest: null
};
}
else{
let list = {
value: arr.shift(),
rest: arrayToList(arr)
};
}
return list;
};
console.log(arrayToList([10, 20]));
//{
//value: 1
//rest: {
//value: 2
//rest: {value: 3, rest: null}
//}
//}
The problem involves scope
Your problem is that you've defined list inside the if/else scope and not outside. This code would work in Python, but Javascript works differently. The solution is to define list in an exterior scope:
let arrayToList = arr => {
let list = {}
if(arr.length == 1)
{
list = {
value: arr.shift(),
rest: null
};
}
else{
list = {
value: arr.shift(),
rest: arrayToList(arr)
};
}
return list;
};
I happen to think it is more readable to do the following:
let arrayToList = arr => {
if(arr.length == 1)
{
return {
value: arr.shift(),
rest: null
};
}
return {
value: arr.shift(),
rest: arrayToList(arr)
};
};
But this is a mostly aesthetic consideration
Recursion is a functional heritage and so using it with functional style yields the best results -
// List.js
const empty =
null
const create = (value, rest = empty) =>
({ value, rest })
const fromArray = (a = []) => // <-- "arrayToList"
a.length === 0
? empty
: create(a[0], fromArray(a.slice(1)))
export { fromArray }
// Main.js
import { fromArray } from "./List"
const mylist =
fromArray([1, 2, 3])
console.log(mylist)
// { value: 1, rest: { value: 2, rest: { value: 3, rest: null } } }
Expand the snippet below to verify the results in your browser -
const empty =
null
const create = (value, rest = empty) =>
({ value, rest })
const fromArray = (a = []) =>
a.length === 0
? empty
: create(a[0], fromArray(a.slice(1)))
const mylist =
fromArray([1,2,3])
console.log(mylist)
// { value: 1, rest: { value: 2, rest: { value: 3, rest: null } } }
let creates blocked scoped variables. Change the declaration to use var.
let arrayToList = arr => {
if(arr.length == 1) {
var list = {
value: arr.shift(),
rest: null
};
} else {
var list = {
value: arr.shift(),
rest: arrayToList(arr)
};
}
return list;
};
console.log(arrayToList([10, 20]));
Or, since you'll want list to start out as an object in either case, you can just set it up prior to the if and populate it based on the condition.
let arrayToList = arr => {
// You know you want list to be an object
// either way, so declare it ahead of time
// and populate it later.
let list = {};
if(arr.length == 1) {
list = {
value: arr.shift(),
rest: null
};
} else {
list = {
value: arr.shift(),
rest: arrayToList(arr)
};
}
return list;
};
console.log(arrayToList([10, 20]));
Thank you in advance for any help.
Why does the return statement from return this.visited inside the if(this.queue.length === 0) code block return undefined? Whereas both console.logs before the return statement return the correct values.
What I tried: I read through a few related post here and then tried binding this to the this.traverse method. I reviewed the syntax, logic, checked for typos several times.
const Tree = require('../Tree/treeExport')
const Queue = require('../Stack_Queue/queueExport')
class BFS extends Tree {
constructor(queue,queue1) {
super()
this.visited = queue
this.queue = queue1
this.traverse = this.traverse.bind(this)
}
traverse() {
let current;
if(this.visited.length < 1) {
this.queue.enqueue(this.root)
}
if(this.queue.length > 0) {
current = this.queue.dequeue()
}
if(current.left) {
this.queue.enqueue(current.left)
}
if(current.right) {
this.queue.enqueue(current.right)
}
if(current) {
this.visited.enqueue(current.value)
}
if(this.queue.length === 0) {
console.log(this.visited, '\n \n')
console.log(this.mapToArray(this.visited), '\n \n')
return this.visited;
} else {
this.traverse()
}
}
mapToArray(queue) {
const array = []
let temp;
while(queue.length > 0) {
temp = queue.dequeue()
array.push(temp)
}
temp = null;
return array;
}
}
let queue = new Queue
let visited = new Queue
const bfs = new BFS(visited,queue)
bfs.insert(5)
bfs.insert(3)
bfs.insert(7)
bfs.insert(2)
bfs.insert(4)
bfs.insert(6)
bfs.insert(9)
console.log(bfs.traverse())
result from console:
Queue {
first: Node { value: 5, next: Node { value: 3, next: [Node] } },
last: Node { value: 9, next: null },
length: 7
}
[
5, 3, 7, 2,
4, 6, 9
]
undefined
I'm trying to turn a nested object that represents my file system into an array of strings that represent the file paths for each folder and file.
Input:
let obj = {
'app': {
'body': {
'abs': {
'muscles.txt': 1
},
'foot.js': 1,
'hand.txt': 1,
'leg.txt': 1
},
'cat.txt': 1,
'dog.js': 1,
'writing': {
'pen.txt': 1,
'phone.txt': 1
}
}
};
Output:
[
'/app',
'/app/body',
'/app/body/abs/',
'/app/body/abs/muscles.txt',
'/app/body/foot.js',
'/app/body/hand.txt',
...
]
What I have so far (it's not working):
function filePaths(obj, oldKey = '', store = []) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
store.push('/' + key);
filePaths(obj[key], key, store);
} else {
store.push('/' + oldKey + '/' + key);
}
}
return store;
}
filePaths(obj);
Here is a working version:
let obj = {
'app': {
'body': {
'abs': {
'muscles.txt': 1
},
'foot.js': 1,
'hand.txt': 1,
'leg.txt': 1
},
'cat.txt': 1,
'dog.js': 1,
'writing': {
'pen.txt': 1,
'phone.txt': 1
}
}
};
function filePaths(obj, prefix = '', store = []) {
for (let key in obj) {
const curPath = `${prefix}/${key}`;
if (typeof obj[key] === 'object') {
store.push(curPath);
filePaths(obj[key], curPath, store);
} else {
store.push(curPath);
}
}
return store;
}
console.log(filePaths(obj));
So I've kept most of your code, but changed the fact that while you kept the "old" key I keep the current path and it serves as a prefix for all the files and as a prefix for all the directories that will get the current key appended.
Here's a recursive solution that leverages the Object.keys method with the spread operator, concat, and map:
let obj = {
'app': {
'body': {
'abs': {
'muscles.txt': 1
},
'foot.js': 1,
'hand.txt': 1,
'leg.txt': 1
},
'cat.txt': 1,
'dog.js': 1,
'writing': {
'pen.txt': 1,
'phone.txt': 1
}
}
};
function filePaths(obj, prefix = "", store = []) {
if (typeof obj !== "object") return [prefix];
return (prefix && [prefix] || []).concat(...Object.keys(obj).map(k => filePaths(obj[k], prefix + "/" + k, store)))
}
console.log(filePaths(obj))
You're on the right track with recursion. For each call, loop over every property in the current object, make a path for the property and add it to the result array. If the property keys to an object, it's a non-terminal node and is called recursively to add paths for its children.
const pathify = (o, res=[], path=[]) => {
for (const dir in o) {
const s = path.join("/");
res.push(`/${s ? `${s}/${dir}` : dir}`);
if (typeof o[dir] === "object") {
pathify(o[dir], res, path.concat(dir));
}
}
return res;
};
const obj = {
'app': {
'body': {
'abs': {
'muscles.txt': 1
},
'foot.js': 1,
'hand.txt': 1,
'leg.txt': 1
},
'cat.txt': 1,
'dog.js': 1,
'writing': {
'pen.txt': 1,
'phone.txt': 1
}
}
};
console.log(pathify(obj));
I applied for a job where I was given this task. I had to write a function reversePrint() which returns a reversed array with values from a passed object.
Here is the object
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
My attempt
function reversePrint(linkedList) {
// empty array with the result
var b = [];
// while loop to check if current next is not null
while ( linkedList.next !== null ) {
// push current value into the array
b.push(linkedList.value);
// go one level deeper
linkedList = linkedList.next;
}
// push the last value into the array
b.push(linkedList.value);
// return reversed array
return b.reverse();
}
The function works, but I have a feeling that there is a better way to do this. I have already searched stackoverflow for javascript recursive operations, but could not find anything that would be considered a duplicate. Is there a more efficient way to do this?
There's nothing fundamentally wrong with your code, but your instinct is right: a recursive solution would seem to match the recursive nature of the data structure better, would be more concise, and also can be written to avoid the reverse.
var someList = {value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null}}}};
function reversePrint(input) {
return !input ? [] : reversePrint(input.next).concat(input.value);
}
console.log(reversePrint(someList));
Note that this solution is not tail-optimizable and might be best avoided if the input could be very deep. A tail-optimizable solution would be:
function reversePrint(input) {
return function inner(input) {
return !input ? [] : [input.value].concat(inner(input.next));
}(input).reverse();
}
Avoiding reverse with the iterative solution is possible, of course, but it requires an expensive unshift onto the front of the array at each step. On the other hand, the recursive solutions create multiple arrays of gradually increasing length, which is not exactly cheap either.
Using recursion you can also do it like this.
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function reversePrint(linkedList) {
var r = []
if(linkedList.next) r = r.concat(reversePrint(linkedList.next))
r.push(linkedList.value)
return r
}
console.log(reversePrint(someList))
https://jsfiddle.net/zwzecvqf/2/
Here you have a recursive function.
function reversePrint (item) {
var nextItem = item.next
var items
if (nextItem) {
items = reversePrint(nextItem)
} else {
items = [];
}
items.push(item.value)
return items
}
Yet another recursive solution, but avoiding creating and throwing away multiple intermediary arrays:
function reversePrint(node, array) {
// Fill in the array if not given
array = array || [];
// Recurse if appropriate
if (node.next) {
reversePrint(node.next, array);
}
// Add the value on the way out
array.push(node.value);
return array;
}
function reversePrint(node, array) {
array = array || [];
if (node.next) {
reversePrint(node.next, array);
}
array.push(node.value);
return array;
}
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
console.log(reversePrint(someList));
What about not using recursion? I didn't find information this is not banned.
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function reversePrint()
{
var currentNode = someList;
var cacheList = [];
do
{
cacheList.push(currentNode.value);
currentNode = currentNode.next;
}
while(currentNode != null);
cacheList.reverse();
return cacheList;
}
var reverseList = reversePrint(); // [4,3,2,1]
Here using a recursive function to sub objects values
function reversePrint(linkedList) {
// empty array with the result
var b = [];
function addValues(subList){
if(linkedList.next)
addValues(linkedList.next);
b.push(item.value);
}
addValues(linkedList);
return b.revers();
}