Как я могу найти массив строк для строки, которая содержит числа из другого массива? - PullRequest
0 голосов
/ 11 февраля 2020

У меня есть два массива:
Массивы длиной два ([0, 0] или [1, 2] массивы) представляют координаты в формате [x, y]:

let coArr = [
  [[0, 0], [1, 1], [2, 2], "Mode1"],
  [[0, 0], [0, 1], [0, 2], "Mode2"]
  // and six more...
], strArr = [
  "Mode1(0, 0)",
  "Mode2(1, 2)",
  "Mode1(0, 2)" 
  // ...
  // You get the idea
  // ...
];

Как я могу создать функцию, которая возвращает true, когда strArr содержит три строки, которые соответствуют массивам в одном из массивов в coArr, , только если Режим одинаков? Кто-нибудь может дать мне некоторый код с объяснением того, как я могу этого достичь?

Вопрос трудно объяснить, но, может быть, поможет пример:

Я хочу вернуть true, если ... давайте скажем:
coArr содержит [[0, 0], [0, 1], [0, 2], "Mode2"]
И strArr содержит 3 строки:

"Mode2(0, 0)",
"Mode2(0, 1)",
"Mode2(0, 2)"

Кроме того, есть ли способ, которым вы не ' t нужно предоставить координаты в качестве аргументов?

1 Ответ

1 голос
/ 12 февраля 2020

Итак, я хочу начать с того, что вам определенно есть чему поучиться прямо сейчас. Всегда полезно сделать несколько попыток, выяснить свой подход, а затем объяснить его любыми кусками кода, которые вы можете предоставить, работает ли он или нет. Ваш вопрос здесь не самый ясный, поэтому я приведу свой пример того, как я его интерпретировал. Я также собираюсь объяснить в несколько нисходящем подходе, так что это немного читается.

Используя введенные вами данные и описание, давайте работать над функция, которая принимает имя режима и возвращает true, если strArr содержит все координаты, которые этот режим имеет в coArr. Чтобы прояснить условия, скажем, что если эта функция возвращает true, мы скажем, что данный режим покрыт на strArr. Надеюсь, ты со мной до сих пор.

const isModeCovered = modeName => {
    let mode = coArr.find(x => x.name == modeName);
    let modeCoordinates = strArr.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

function isModeCovered(modeName) {
    let mode = coArr.find(function(x) {
        return x.name == modeName;
    });
    let modeCoordinates = strArr.filter(function(x) {
        return x.name == modeName;
    });
    return mode.isCoveredBy(modeCoordinates);
}

Здесь у нас есть функция, которую я написал для этого. Как вы можете видеть, я написал эту функцию дважды: один раз, используя функции стрелок и один раз используя "нормальные" функции. Я не собираюсь углубляться в go, чтобы узнать, что такое функция Arrow в Javascript, и вы увидите, что в остальной части этого примера они будут использованы. Если вам необходимо ознакомиться с ними сейчас, самое время это сделать, но вы также можете понять их, просто сравнив две функции выше. В любом случае, давайте продолжим и посмотрим, что на самом деле делает эта функция.

let mode = coArr.find(x => x.name == modeName);

Здесь мы используем функцию find, чтобы найти первый элемент в coArr имя которого совпадает с именем данного режима. Как видно из ссылки, find ищет каждый элемент Array, над которым он выполняется, и возвращает первый элемент, который возвращает заданный обратный вызов. Обратный вызов, который мы ему дали , не будет работать . Это потому, что предполагается, что каждый элемент coArr является объектом, который имеет name. Следующая строка, где мы фильтруем strArr, также будет не работать по аналогичной причине.

Почему я это сделал?

Я сделал это, потому что данные в том виде, в каком они были предоставлены, - если быть тупым - головная боль. Вы можете абсолютно решить эту проблему без изменения данных, но есть много причин, по которым я предпочитаю это, я объясню позже. Итак, мы собираемся предположить, что мы взяли данные и проанализировали в более удобные для использования формы:

const modes = coArr.map(x => new Mode(x));
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

Хорошо, вот немного измененный код. Мы видим, что сопоставляет coArr с новым массивом объекта с именами Mode и сопоставляет strArr с новым массивом объекта называется ModeCoordinate. Мы можем беспокоиться о , как конструктор для этих объектов обрабатывает это позже, но для ясности здесь кратко дано представление о том, как эти объекты выглядят (в псевдо-JS это не скомпилируется) :

class Coordinate {
    int x,
    int y
}

class Mode {
    string name,
    Coordinate[] coordinates // an Array of Coordinates
}

class ModeCoordinate {
    string name,
    Coordinate coordinate
}

Если наши данные находятся в Array с Mode с и ModeCoordinate с, мы можем легко искать по их имени и находить их координаты. Как только мы можем легко это сделать, большая часть работы для нас сделана, и нам просто нужно определить детали логики c за последней строкой функции:

return mode.isCoveredBy(modeCoordinates);

Давайте добавим эту функцию, isCoveredBy до Mode класса:

class Mode {
    string name,
    Coordinate[] coordinates

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

Это короткий метод, но давайте быстро go пройдемся по нему. Мы используем от for of до l oop через каждые Coordinate в Mode в coordinates. Затем мы используем if, чтобы проверить, можем ли мы найти, что Coordinate в Array из заданных Coordinate с (которые, если вы помните, взяты из strArr, которые имеют то же имя режима, что и Mode мы находимся). Если мы не можем найти его, мы немедленно возвращаем false, потому что для того, чтобы режим был "покрыт", мы заявили, что каждая отдельная координата должна также существовать в strArr. Если он не возвратил false в любой момент, мы возвращаем true в конце l oop, потому что это означает, что они все были найдены.

Есть вероятность, что эта строка, условие if, может сбить с толку:

!!!modeCoordinates.find(c => c.coordinate.equals(coordinate))

Мы можем кратко разобрать это. modeCoordinates.find(c => c.coordinate.equals(coordinate)) принимает modeCoordinates, который, если вы помните, является ModeCoordinate Array, в который мы сопоставили все наши strArr элементы и отфильтровали его по имени режима Mode, в котором мы находимся, и пытаясь найти тот, который имеет Coordinate, равный координате нашей текущей итерации l oop. Возвращает элемент, если он найден, или ложь, если не найден. !!! перед ним является комбинацией обычного оператора ! или нет и оператора !!, который переводит возвращаемое значение в логическое значение.

Итак, теперь мы знаем, что нам нужно проанализировать данные в наших более хороших типах объектов, чтобы облегчить поиск и фильтрацию, и мы создали функцию, которая может проверять, является ли данное имя режима охватываемым . Осталось только выполнить фактический анализ, который мы уже упоминали, можно сделать в constructors наших классов .

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

Наш класс Coordinate, используемый только внутренне другими нашими классами, это красиво и просто. Его конструктор принимает x и ay и разбирает их на целые числа. Я добавил функцию equals, чтобы мы могли легко сравнить, если два Coordinate одинаковы.

class Mode {
    constructor(arr) {
        this.name = arr.pop();
        this.coordinates = arr.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

Наш конструктор класса Mode принимает Array (поскольку coArr является Array из Array с) и вставляет последний член в свое имя , Оставшиеся Array - это все координаты, поэтому они отображаются в Coordinate объекты.

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

Наш класс ModeCoordinate ожидает строку и использует regex для удаления пробелов (используя заменить ), а затем разбить строку и оставить только буквы c символов. Так, например, "Mode2(1, 2)" становится ["Mode2", "1", "2"]. Это позволяет легко сохранить первый элемент в качестве имени и создать Coordinate из двух последних.

Теперь мы можем собрать все это вместе. Я добавил несколько тестовых данных, чтобы вы могли их запустить:

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

class Mode {
    constructor(arr) {
        this.name = arr.pop();
        this.coordinates = arr.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

let coArr = [
    [[0, 0], [1, 1], [2, 2], "Mode1"],
    [[0, 0], [0, 1], [0, 2], "Mode2"],
    [[0, 0], [0, 1], [0, 2], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode4"],
    [[0, 0], [0, 1], [0, 2], "Mode5"],
    [[0, 0], [0, 1], [0, 2], "Mode6"]
  ], strArr = [
    "Mode1(0, 0)",
    "Mode1(1, 1)",
    "Mode1(2, 2)",
    "Mode2(0, 0)",
    "Mode2(1, 2)",
    "Mode2(0, 2)"
  ];

const modes = coArr.map(x => new Mode(x));
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

console.log(isModeCovered("Mode1")); // returns true
console.log(isModeCovered("Mode2")); // returns false

Если вы хотите увидеть, покрыты ли все режимы, вы можете запустить функцию, которую мы создали в al oop, для всех режимов в modes. Очевидно, что здесь нет обработки ошибок - мы действительно ожидаем, что данные будут в формате, который вы показали. Надеемся, что это ответ на ваш вопрос.

Изменить с альтернативным решением, как требуется в комментариях:

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

class Mode {
    constructor(name, coordinates) {
        this.name = name;
        this.coordinates = coordinates.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }

    addCoordinates(coordinates) {
        let newCoordinates = coordinates.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
        this.coordinates.concat(newCoordinates);
    }
}

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

let coArr = [
    [[0, 0], [1, 1], [2, 2], "Mode1"],
    [[0, 0], [2, 2], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode2"],
    [[1, 1], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode4"],
    [[0, 0], [0, 1], [0, 2], "Mode5"],
    [[0, 0], [0, 1], [0, 2], "Mode6"]
  ], strArr = [
    "Mode1(0, 0)",
    "Mode1(1, 1)",
    "Mode1(2, 2)",
    "Mode2(0, 0)",
    "Mode2(1, 2)",
    "Mode2(0, 2)",
    "Mode3(0, 0)",
    "Mode3(1, 1)",
    "Mode3(2, 2)"
  ];

const extractModes = () => {
    let modes = [];
    for (let item of coArr) {
        let name = item.pop();
        let mode = modes.find(x => x.name == name);
        if (!!mode)
            mode.addCoordinates(item);
        else 
            modes.push(new Mode(name, item));      
    }

    return modes;
};

const modes = extractModes();
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

console.log(isModeCovered("Mode1")); // returns true
console.log(isModeCovered("Mode2")); // returns false
console.log(isModeCovered("Mode3")); // returns true
...